diff options
author | Tristan Gingold <tgingold@free.fr> | 2021-04-28 21:21:28 +0200 |
---|---|---|
committer | Tristan Gingold <tgingold@free.fr> | 2021-04-28 21:39:05 +0200 |
commit | 6bc0f927f803debc51d5bb9bed46cf18b5a0435c (patch) | |
tree | 8f44b0003de36cd75ab1444196ea7eadc81aa626 /src | |
parent | 4f7aaa50f93b667aee662de5f9308545bb00a5f2 (diff) | |
download | ghdl-6bc0f927f803debc51d5bb9bed46cf18b5a0435c.tar.gz ghdl-6bc0f927f803debc51d5bb9bed46cf18b5a0435c.tar.bz2 ghdl-6bc0f927f803debc51d5bb9bed46cf18b5a0435c.zip |
synth: add verilog output
Diffstat (limited to 'src')
-rw-r--r-- | src/ghdldrv/ghdlsynth.adb | 6 | ||||
-rw-r--r-- | src/synth/netlists-disp_verilog.adb | 1396 | ||||
-rw-r--r-- | src/synth/netlists-disp_verilog.ads | 21 |
3 files changed, 1423 insertions, 0 deletions
diff --git a/src/ghdldrv/ghdlsynth.adb b/src/ghdldrv/ghdlsynth.adb index 7a55cafee..890be63ec 100644 --- a/src/ghdldrv/ghdlsynth.adb +++ b/src/ghdldrv/ghdlsynth.adb @@ -43,6 +43,7 @@ with Vhdl.Utils; with Netlists.Dump; with Netlists.Disp_Vhdl; +with Netlists.Disp_Verilog; with Netlists.Disp_Dot; with Netlists.Errors; @@ -56,6 +57,7 @@ package body Ghdlsynth is (Format_Default, Format_Raw, Format_Dump, Format_Dot, Format_Vhdl, Format_Raw_Vhdl, + Format_Verilog, Format_None); type Name_Id_Array is array (Natural range <>) of Name_Id; @@ -203,6 +205,8 @@ package body Ghdlsynth is Cmd.Oformat := Format_Vhdl; elsif Option = "--out=raw-vhdl" then Cmd.Oformat := Format_Raw_Vhdl; + elsif Option = "--out=verilog" then + Cmd.Oformat := Format_Verilog; elsif Option = "-di" then Flag_Debug_Noinference := True; elsif Option = "-dc" then @@ -439,6 +443,8 @@ package body Ghdlsynth is Synth.Disp_Vhdl.Disp_Vhdl_Wrapper (Ent, Res, Inst); when Format_Raw_Vhdl => Netlists.Disp_Vhdl.Disp_Vhdl (Res); + when Format_Verilog => + Netlists.Disp_Verilog.Disp_Verilog (Res); end case; end Disp_Design; diff --git a/src/synth/netlists-disp_verilog.adb b/src/synth/netlists-disp_verilog.adb new file mode 100644 index 000000000..e9b320b9b --- /dev/null +++ b/src/synth/netlists-disp_verilog.adb @@ -0,0 +1,1396 @@ +-- Disp a netlist in verilog. +-- Copyright (C) 2021 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 Simple_IO; use Simple_IO; +with Utils_IO; use Utils_IO; +with Types_Utils; use Types_Utils; +with Files_Map; + +with Netlists.Utils; use Netlists.Utils; +with Netlists.Iterators; use Netlists.Iterators; +with Netlists.Gates; use Netlists.Gates; +with Netlists.Locations; +with Netlists.Dump; use Netlists.Dump; + +package body Netlists.Disp_Verilog is + Flag_Merge_Lit : constant Boolean := True; + Flag_Merge_Edge : constant Boolean := True; + + procedure Put_Type (W : Width) is + begin + if W > 1 then + Put ("["); + Put_Uns32 (W - 1); + Put (":0] "); + end if; + end Put_Type; + + procedure Put_Name_Version (N : Sname) is + begin + Put_Uns32 (Get_Sname_Version (N)); + end Put_Name_Version; + + procedure Put_Name_1 (N : Sname) + is + Prefix : Sname; + begin + -- Do not crash on No_Name. + if N = No_Sname then + Put ("*nil*"); + return; + end if; + + Prefix := Get_Sname_Prefix (N); + if Prefix /= No_Sname then + Put_Name_1 (Prefix); + Put ("_"); + end if; + + case Get_Sname_Kind (N) is + when Sname_User => + Put_Id (Get_Sname_Suffix (N)); + when Sname_Artificial => + Put_Id (Get_Sname_Suffix (N)); + when Sname_Version => + Put ("n"); + Put_Name_Version (N); + end case; + end Put_Name_1; + + procedure Put_Name (N : Sname) is + begin + -- Do not crash on No_Name. + if N = No_Sname then + Put ("*nil*"); + return; + end if; + + if Get_Sname_Kind (N) = Sname_User + and then Get_Sname_Prefix (N) = No_Sname + then + Put_Id (Get_Sname_Suffix (N)); + else + Put_Name_1 (N); + end if; + end Put_Name; + + procedure Put_Interface_Name (N : Sname) is + begin + -- Do not crash on No_Name. + if N = No_Sname then + Put ("*nil*"); + return; + end if; + + -- Interface names are not versionned. + if Get_Sname_Kind (N) in Sname_User .. Sname_Artificial then + Put_Name (N); + else + Put ("*err*"); + end if; + end Put_Interface_Name; + + procedure Disp_Net_Name (N : Net) is + begin + if N = No_Net then + Put ("<unassigned>"); + return; + end if; + + declare + Inst : constant Instance := Get_Net_Parent (N); + Idx : constant Port_Idx := Get_Port_Idx (N); + M : Module; + Inst_Name : Sname; + Port_Name : Sname; + begin + if Is_Self_Instance (Inst) then + -- For ports of the current module, simply use the port name. + Put_Name (Get_Input_Desc (Get_Module (Inst), Idx).Name); + else + Inst_Name := Get_Instance_Name (Inst); + Put_Name (Inst_Name); + M := Get_Module (Inst); + case Get_Id (M) is + when Id_Signal + | Id_Isignal => + -- No suffix for signals (it's 'o'). + null; + when others => + Port_Name := Get_Output_Desc (M, Idx).Name; + Put ("_"); + Put_Interface_Name (Port_Name); + end case; + end if; + end; + end Disp_Net_Name; + + procedure Disp_Instance_Gate (Inst : Instance) + is + Imod : constant Module := Get_Module (Inst); + Idx : Port_Idx; + Max_Idx : Port_Idx; + Name : Sname; + First : Boolean; + Param : Param_Desc; + begin + Put (" "); + + -- Gate name + Name := Get_Module_Name (Imod); + if Get_Id (Imod) < Id_User_None then + Put (" gate_"); + pragma Assert (Get_Sname_Kind (Name) = Sname_Artificial + and then Get_Sname_Prefix (Name) = No_Sname); + Put_Id (Get_Sname_Suffix (Name)); + else + Put_Name (Name); + end if; + + -- Instance name + Put (" "); + Name := Get_Instance_Name (Inst); + if Get_Sname_Kind (Name) = Sname_Version then + Put ("inst_"); + Put_Name_Version (Name); + else + Put_Name (Name); + end if; + + if Get_Nbr_Params (Imod) /= 0 then + Put_Line (" #("); + for P in 1 .. Get_Nbr_Params (Inst) loop + Param := Get_Param_Desc (Imod, P - 1); + if P > 1 then + Put_Line (","); + end if; + Put (" ."); + Put_Interface_Name (Param.Name); + Put ("("); + case Param.Typ is + when Param_Uns32 => + Put_Uns32 (Get_Param_Uns32 (Inst, P - 1)); + when Param_Types_Pval => + Disp_Pval_Binary (Get_Param_Pval (Inst, P - 1)); + when Param_Invalid => + Put ("*invalid*"); + end case; + Put (")"); + end loop; + Put_Line (")"); + Put_Line (" ("); + else + Put_Line (" ("); + end if; + + First := True; + -- Inputs + Idx := 0; + Max_Idx := Get_Nbr_Inputs (Imod); + for I of Inputs (Inst) loop + if First then + First := False; + else + Put_Line (","); + end if; + Put (" "); + if Idx < Max_Idx then + Put ("."); + Put_Interface_Name (Get_Input_Desc (Imod, Idx).Name); + Put ("("); + end if; + Disp_Net_Name (Get_Driver (I)); + if Idx < Max_Idx then + Put (")"); + Idx := Idx + 1; + end if; + end loop; + -- Outputs + Idx := 0; + for O of Outputs (Inst) loop + if First then + First := False; + else + Put_Line (","); + end if; + Put (" ."); + Put_Interface_Name (Get_Output_Desc (Imod, Idx).Name); + Idx := Idx + 1; + Put ("("); + declare + I : Input; + begin + I := Get_First_Sink (O); + if I = No_Input then + Put ("open"); + else + Disp_Net_Name (O); + end if; + end; + Put (")"); + end loop; + Put_Line (");"); + end Disp_Instance_Gate; + + procedure Disp_Binary_Lit (Va : Uns32; Zx : Uns32; Wd : Width) is + begin + Put_Uns32 (Wd); + Put ("'b"); + Disp_Binary_Digits (Va, Zx, Natural (Wd)); + end Disp_Binary_Lit; + + procedure Disp_Const_Bit (Inst : Instance) + is + W : constant Width := Get_Width (Get_Output (Inst, 0)); + Nd : constant Width := W / 32; + Ld : constant Natural := Natural (W mod 32); + begin + Put ('"'); + if Ld > 0 then + Disp_Binary_Digits (Get_Param_Uns32 (Inst, Param_Idx (Nd)), 0, Ld); + end if; + for I in reverse 1 .. Nd loop + Disp_Binary_Digits + (Get_Param_Uns32 (Inst, Param_Idx (I - 1)), 0, 32); + end loop; + Put ('"'); + end Disp_Const_Bit; + + procedure Disp_Const_Log (Inst : Instance) + is + W : constant Width := Get_Width (Get_Output (Inst, 0)); + Nd : constant Width := W / 32; + Ld : constant Natural := Natural (W mod 32); + begin + Put ('"'); + if Ld > 0 then + Disp_Binary_Digits (Get_Param_Uns32 (Inst, Param_Idx (2 * Nd)), + Get_Param_Uns32 (Inst, Param_Idx (2 * Nd + 1)), + Ld); + end if; + for I in reverse 1 .. Nd loop + Disp_Binary_Digits + (Get_Param_Uns32 (Inst, Param_Idx (2 * (I - 1))), + Get_Param_Uns32 (Inst, Param_Idx (2 * (I - 1)) + 1), + 32); + end loop; + Put ('"'); + end Disp_Const_Log; + + procedure Disp_X_Lit (W : Width; C : Character) is + begin + Put_Uns32 (W); + Put ("'b"); + Put (C); + end Disp_X_Lit; + + procedure Disp_Extract (Inst : Instance); + + procedure Disp_Constant_Inline (Inst : Instance) + is + Imod : constant Module := Get_Module (Inst); + O : constant Net := Get_Output (Inst, 0); + begin + case Get_Id (Imod) is + when Id_Const_UB32 + | Id_Const_SB32 => + Disp_Binary_Lit (Get_Param_Uns32 (Inst, 0), 0, Get_Width (O)); + when Id_Const_UL32 => + Disp_Binary_Lit (Get_Param_Uns32 (Inst, 0), + Get_Param_Uns32 (Inst, 1), + Get_Width (O)); + when Id_Const_Z => + Disp_X_Lit (Get_Width (O), 'Z'); + when Id_Const_X => + Disp_X_Lit (Get_Width (O), 'X'); + when Id_Const_Bit => + Disp_Const_Bit (Inst); + when Id_Const_Log => + Disp_Const_Log (Inst); + when Id_Extract => + Disp_Extract (Inst); + when others => + raise Internal_Error; + end case; + end Disp_Constant_Inline; + + procedure Disp_Const_Bit (Inst : Instance; Off : Uns32) + is + Val : Uns32; + Zx : Uns32; + begin + case Get_Id (Inst) is + when Id_Const_Bit => + Zx := 0; + Val := Get_Param_Uns32 (Inst, Param_Idx (Off / 32)); + Val := Shift_Right (Val, Natural (Off mod 32)) and 1; + when Id_Const_Log => + Zx := Get_Param_Uns32 (Inst, 2 * Param_Idx (Off / 32) + 1); + Zx := Shift_Right (Zx, Natural (Off mod 32)) and 1; + Val := Get_Param_Uns32 (Inst, 2 * Param_Idx (Off / 32)); + Val := Shift_Right (Val, Natural (Off mod 32)) and 1; + when Id_Const_UB32 => + Zx := 0; + if Off < 32 then + Val := Get_Param_Uns32 (Inst, 0); + Val := Shift_Right (Val, Natural (Off mod 32)) and 1; + else + Val := 0; + end if; + when Id_Const_UL32 => + if Off < 32 then + Val := Get_Param_Uns32 (Inst, 0); + Val := Shift_Right (Val, Natural (Off mod 32)) and 1; + Zx := Get_Param_Uns32 (Inst, 1); + Zx := Shift_Right (Zx, Natural (Off mod 32)) and 1; + else + Val := 0; + Zx := 0; + end if; + when Id_Const_X => + Zx := 1; + Val := 1; + when others => + raise Internal_Error; + end case; + Put (Bchar (Zx * 2 + Val)); + end Disp_Const_Bit; + + procedure Disp_Memory_Init (Port : Net; Val : Net; W : Width; Depth : Width) + is + Inst : constant Instance := Get_Net_Parent (Val); + begin + case Get_Id (Inst) is + when Id_Const_X => + return; + when others => + null; + end case; + + Put_Line (" initial begin"); + for I in reverse 0 .. Depth - 1 loop + Put (" "); + Disp_Net_Name (Port); + Put ("["); + Put_Uns32 (I); + Put ("] = "); + Put_Uns32 (W); + Put ("'b"); + for J in reverse 0 .. W - 1 loop + Disp_Const_Bit (Inst, I * W + J); + end loop; + Put_Line (";"); + end loop; + Put_Line (" end"); + end Disp_Memory_Init; + + function Need_Name (Inst : Instance) return Boolean + is + Id : constant Module_Id := Get_Id (Inst); + begin + case Id is + when Id_Extract + | Id_Dyn_Extract + | Id_Dyn_Insert + | Id_Utrunc + | Id_Strunc => + return True; + when Id_User_None .. Module_Id'Last => + return True; + when others => + return False; + end case; + end Need_Name; + + -- Return True if constant INST is connected to an instance that needs + -- a name. In that case, a signal will be created and driven. + function Need_Signal (Inst : Instance) return Boolean + is + I : Input; + begin + I := Get_First_Sink (Get_Output (Inst, 0)); + while I /= No_Input loop + if Need_Name (Get_Input_Parent (I)) then + return True; + end if; + I := Get_Next_Sink (I); + end loop; + return False; + end Need_Signal; + + -- Return TRUE if edge INST (posedge or negedge) is used outside clock + -- inputs. + function Need_Edge (Inst : Instance) return Boolean + is + I : Input; + Parent : Instance; + begin + I := Get_First_Sink (Get_Output (Inst, 0)); + while I /= No_Input loop + Parent := Get_Input_Parent (I); + case Get_Id (Parent) is + when Id_Dff + | Id_Adff + | Id_Idff + | Id_Iadff => + if I /= Get_Input (Parent, 0) then + return True; + end if; + when Id_Mem_Rd_Sync + | Id_Mem_Wr_Sync => + if I /= Get_Input (Parent, 2) then + return True; + end if; + when others => + return True; + end case; + I := Get_Next_Sink (I); + end loop; + return False; + end Need_Edge; + + type Conv_Type is + (Conv_None, Conv_Unsigned, Conv_Signed, Conv_Edge, Conv_Clock); + + procedure Disp_Net_Expr (N : Net; Inst : Instance; Conv : Conv_Type) + is + Net_Inst : Instance; + begin + if N = No_Net then + Put ("<unassigned>"); + return; + end if; + + Net_Inst := Get_Net_Parent (N); + if Flag_Merge_Lit + and then Get_Id (Net_Inst) in Constant_Module_Id + and then not Need_Name (Inst) + then + case Conv is + when Conv_None => + Disp_Constant_Inline (Net_Inst); + when Conv_Unsigned => + Put ("$unsigned("); + Disp_Constant_Inline (Net_Inst); + Put (")"); + when Conv_Signed => + Put ("$signed("); + Disp_Constant_Inline (Net_Inst); + Put (")"); + when Conv_Edge + | Conv_Clock => + -- Not expected: a constant is not an edge. + raise Internal_Error; + end case; + else + case Conv is + when Conv_None => + Disp_Net_Name (N); + when Conv_Edge => + case Edge_Module_Id (Get_Id (Net_Inst)) is + when Id_Posedge => + Put ("posedge "); + when Id_Negedge => + Put ("negedge "); + end case; + Disp_Net_Name (Get_Input_Net (Net_Inst, 0)); + when Conv_Clock => + Disp_Net_Name (Get_Input_Net (Net_Inst, 0)); + when Conv_Unsigned => + Put ("$unsigned("); + Disp_Net_Name (N); + Put (")"); + when Conv_Signed => + Put ("$signed("); + Disp_Net_Name (N); + Put (")"); + end case; + end if; + end Disp_Net_Expr; + + NL : constant Character := ASCII.LF; + + type Uns32_Array is array (Natural range <>) of Uns32; + No_Uns32_Arr : constant Uns32_Array := (1 .. 0 => 0); + + -- Template: + -- \[C]AN + -- C: conversion u: unsigned, s: signed, f: force logic + -- A: argument o: output, i: input, n: value, p: parameter, l: label + -- N: argument number (0-9) + procedure Disp_Template + (S : String; Inst : Instance; Val : Uns32_Array := No_Uns32_Arr) + is + I : Positive; + C : Character; + Idx : Natural; + N : Net; + Conv : Conv_Type; + V : Uns32; + begin + I := S'First; + while I <= S'Last loop + C := S (I); + -- Escape character. + if C = '\' then + I := I + 1; + -- Conversion (optional). + case S (I) is + when 'u' => + Conv := Conv_Unsigned; + I := I + 1; + when 's' => + Conv := Conv_Signed; + I := I + 1; + when 'e' => + Conv := Conv_Edge; + I := I + 1; + when 'c' => + Conv := Conv_Clock; + I := I + 1; + when others => + Conv := Conv_None; + end case; + Idx := Character'Pos (S (I + 1)) - Character'Pos ('0'); + case S (I) is + when 'o' => + pragma Assert (Conv = Conv_None); + N := Get_Output (Inst, Port_Idx (Idx)); + Disp_Net_Name (N); + when 'i' => + N := Get_Input_Net (Inst, Port_Idx (Idx)); + Disp_Net_Expr (N, Inst, Conv); + when 'n' => + V := Val (Idx); + Put_Uns32 (V); + when 'p' => + V := Get_Param_Uns32 (Inst, Param_Idx (Idx)); + case Conv is + when Conv_None + | Conv_Unsigned => + Put_Uns32 (V); + when Conv_Signed => + Put_Int32 (To_Int32 (V)); + when Conv_Edge + | Conv_Clock => + raise Internal_Error; + end case; + when 'l' => + pragma Assert (Idx = 0); + pragma Assert (Conv = Conv_None); + Put_Name (Get_Instance_Name (Inst)); + when others => + raise Internal_Error; + end case; + + I := I + 2; + else + Put (C); + I := I + 1; + end if; + end loop; + end Disp_Template; + + procedure Disp_Extract (Inst : Instance) + is + O : constant Net := Get_Output (Inst, 0); + I : constant Net := Get_Input_Net (Inst, 0); + Wd : constant Width := Get_Width (O); + Off : constant Uns32 := Get_Param_Uns32 (Inst, 0); + begin + Disp_Template ("\i0", Inst); + if Get_Width (I) > 1 then + -- If width is 1, the signal is declared as a scalar and + -- therefore cannot be indexed. + if Wd > 1 then + Disp_Template ("[\n0:\n1]", Inst, + (0 => Off + Wd - 1, 1 => Off)); + elsif Wd = 1 then + Disp_Template ("[\n0]", Inst, (0 => Off)); + else + Disp_Template (" (-1 downto 0)", Inst); + end if; + end if; + end Disp_Extract; + + procedure Disp_Memory (Mem : Instance) + is + Ports : constant Net := Get_Output (Mem, 0); + Port : Net; + Port_Inst : Instance; + S : Net; + Data_W : Width; + Depth : Uns32; + begin + -- Display a process, with as sensitivity elements: + -- * write clocks + -- * read address + -- As statements: + Data_W := 0; + Port := Ports; + loop + Port_Inst := Get_Input_Parent (Get_First_Sink (Port)); + case Get_Id (Port_Inst) is + when Id_Mem_Wr_Sync => + -- Clock + S := Get_Input_Net (Port_Inst, 2); + -- Strip the edge. + S := Get_Input_Net (Get_Net_Parent (S), 0); + Data_W := Get_Width (Get_Input_Net (Port_Inst, 4)); + when Id_Mem_Rd => + -- Address + S := Get_Input_Net (Port_Inst, 1); + Data_W := Get_Width (Get_Output (Port_Inst, 1)); + when Id_Mem_Rd_Sync => + -- Clock + S := Get_Input_Net (Port_Inst, 2); + -- Strip the edge. + S := Get_Input_Net (Get_Net_Parent (S), 0); + Data_W := Get_Width (Get_Output (Port_Inst, 1)); + when Id_Memory + | Id_Memory_Init => + exit; + when others => + raise Internal_Error; + end case; + Port := Get_Output (Port_Inst, 0); + end loop; + + Depth := Get_Width (Ports) / Data_W; + + -- Declare the memory. + Put (" reg "); + Put_Type (Data_W); + Disp_Net_Name (Ports); + Put_Type (Depth); + Put_Line("; // memory"); + + -- Initialization + if Get_Id (Mem) = Id_Memory_Init then + declare + Val : Net; + Val_Inst : Instance; + begin + Val := Get_Input_Net (Mem, 1); + Val_Inst := Get_Net_Parent (Val); + if Get_Id (Val_Inst) = Id_Isignal then + Val := Get_Input_Net (Val_Inst, 1); + end if; + Disp_Memory_Init (Ports, Val, Data_W, Depth); + end; + end if; + + Port := Ports; + loop + Port_Inst := Get_Input_Parent (Get_First_Sink (Port)); + case Get_Id (Port_Inst) is + when Id_Mem_Wr_Sync => + Disp_Template + (" always @(\ei2)" & NL & + " if (\i3)" & NL, Port_Inst); + Disp_Template + (" \o0", Mem); + Disp_Template ("[\i1] <= \i4;" & NL, Port_Inst); + when Id_Mem_Rd => + Disp_Template (" assign \o1 = ", Port_Inst); + Disp_Template ("\o0", Mem); + Disp_Template ("[\i1];" & NL, Port_Inst); + when Id_Mem_Rd_Sync => + Disp_Template + (" always @(\ei2)" & NL & + " if (\i3)" & NL & + " \o1 <= ", Port_Inst); + Disp_Template ("\o0", Mem); + Disp_Template ("[\i1];" & NL, Port_Inst); + when Id_Memory + | Id_Memory_Init => + exit; + when others => + raise Internal_Error; + end case; + Port := Get_Output (Port_Inst, 0); + end loop; + end Disp_Memory; + + procedure Disp_Pmux (Inst : Instance) + is + Def : constant Net := Get_Input_Net (Inst, 0); + W : constant Width := Get_Width (Def); + begin + Disp_Template (" always @*" & NL & + " case (\i0)" & NL, Inst); + for I in 1 .. W loop + Put (" "); + -- One hot encoding. + Put_Uns32 (W); + Put ("'b"); + for J in 1 .. W loop + if I = J then + Put ('1'); + else + Put ('0'); + end if; + end loop; + Disp_Template (": \o0 <= ", Inst); + Disp_Net_Expr + (Get_Input_Net (Inst, Port_Idx (2 + W - I)), Inst, Conv_None); + Put_Line (";"); + end loop; + Disp_Template (" endcase" & NL, Inst); + end Disp_Pmux; + + procedure Disp_Instance_Inline (Inst : Instance) + is + Imod : constant Module := Get_Module (Inst); + Loc : constant Location_Type := Locations.Get_Location (Inst); + Id : constant Module_Id := Get_Id (Imod); + begin + if Loc /= No_Location then + declare + File : Name_Id; + Line : Positive; + Col : Natural; + begin + Files_Map.Location_To_Position (Loc, File, Line, Col); + Put (" /* "); + Put_Id (File); + Put (':'); + Put_Uns32 (Uns32 (Line)); + Put (':'); + Put_Uns32 (Uns32 (Col)); + Put (" */"); + New_Line; + end; + end if; + case Id is + when Id_Memory + | Id_Memory_Init => + Disp_Memory (Inst); + when Id_Mem_Rd + | Id_Mem_Rd_Sync + | Id_Mem_Wr_Sync => + null; + when Id_Output => + Disp_Template (" assign \o0 = \i0; // (output)" & NL, Inst); + when Id_Inout + | Id_Iinout => + -- Gates inout are special: output 1 must be connected to an + -- output (with the is_inout flag set) of the module. + Disp_Template (" assign \o1 = \i0; // (inout - port)" & NL, Inst); + Disp_Template (" assign \o0 = ", Inst); + declare + Inp : constant Input := Get_First_Sink (Get_Output (Inst, 1)); + Iinst : constant Instance := Get_Input_Parent (Inp); + begin + Put_Name (Get_Output_Name (Get_Module (Iinst), + Get_Port_Idx (Inp))); + end; + Put ("; // (inout - read)" & NL); + when Id_Signal => + Disp_Template (" assign \o0 = \i0; // (signal)" & NL, Inst); + when Id_Isignal => + if Get_Driver (Get_Input (Inst, 0)) /= No_Net then + -- It is possible (and meaningful) to have unassigned + -- isignal. + Disp_Template (" assign \o0 = \i0; // (isignal)" & NL, Inst); + end if; + when Id_Port => + Disp_Template (" \o0 <= \i0; -- (port)" & NL, Inst); + when Id_Nop => + Disp_Template (" \o0 <= \i0; -- (nop)" & NL, Inst); + when Id_Enable => + Disp_Template (" \o0 <= \i0; -- (enable)" & NL, Inst); + when Id_Not => + Disp_Template (" assign \o0 = ~\i0;" & NL, Inst); + when Id_Neg => + Disp_Template (" assign \o0 = -\i0;" & NL, Inst); + when Id_Abs=> + Disp_Template (" \o0 <= std_logic_vector(abs \si0);" & NL, Inst); + when Id_Extract => + Disp_Template (" assign \o0 = ", Inst); + Disp_Extract (Inst); + Put_Line (";"); + when Id_Memidx => + declare + O : constant Net := Get_Output (Inst, 0); + Wd : constant Width := Get_Width (O); + Step : constant Uns32 := Get_Param_Uns32 (Inst, 0); + begin + if Step /= 1 then + Disp_Template + (" \o0 <= std_logic_vector (resize (resize (", Inst); + if Get_Width (Get_Input_Net (Inst, 0)) = 1 then + Disp_Template ("unsigned'(0 => \i0)", Inst); + else + Disp_Template ("\ui0", Inst); + end if; + Disp_Template + (", \n0) * \up0, \n0));" & NL, Inst, (0 => Wd)); + else + Disp_Template (" \o0 <= \i0;" & NL, Inst); + end if; + end; + when Id_Addidx => + declare + W0 : constant Width := Get_Width (Get_Input_Net (Inst, 0)); + W1 : constant Width := Get_Width (Get_Input_Net (Inst, 1)); + begin + if W0 > W1 then + Disp_Template + (" \o0 <= std_logic_vector (\ui0 + resize(\ui1, \n0));" + & NL, Inst, (0 => W0)); + elsif W0 < W1 then + Disp_Template + (" \o0 <= std_logic_vector (resize (\ui0, \n0) + \ui1);" + & NL, Inst, (0 => W1)); + else + pragma Assert (W0 = W1); + Disp_Template + (" \o0 <= std_logic_vector (\ui0 + \ui1);" + & NL, Inst); + end if; + end; + when Id_Dyn_Extract => + declare + O : constant Net := Get_Output (Inst, 0); + Wd : constant Width := Get_Width (O); + Off : constant Uns32 := Get_Param_Uns32 (Inst, 0); + begin + Disp_Template (" \o0 <= \i0", Inst); + if Wd /= 0 then + Disp_Template (" (to_integer (\ui1)", Inst); + if Off /= 0 then + Disp_Template (" + \n0", Inst, (0 => Off)); + end if; + if Wd > 1 then + Disp_Template (" + \n0 - 1 downto to_integer (\ui1)", + Inst, (0 => Wd)); + if Off /= 0 then + Disp_Template (" + \n0", Inst, (0 => Off)); + end if; + end if; + Put (")"); + end if; + Put_Line (";"); + end; + when Id_Dyn_Insert + | Id_Dyn_Insert_En => + declare + -- I0: Input, I1: Value, I2: position + -- P0: offset + I0 : constant Net := Get_Input_Net (Inst, 0); + I1 : constant Net := Get_Input_Net (Inst, 1); + I2 : constant Net := Get_Input_Net (Inst, 2); + Iarr : constant Net_Array (0 .. 2) := (I0, I1, I2); + Iw : constant Width := Get_Width (Get_Input_Net (Inst, 1)); + First : Boolean; + begin + Put (" process ("); + First := True; + for I in Iarr'Range loop + if (Get_Id (Get_Net_Parent (Iarr (I))) + not in Constant_Module_Id) + then + if First then + First := False; + else + Put (", "); + end if; + Disp_Net_Name (Iarr (I)); + end if; + end loop; + Put (")" & NL); + Disp_Template + (" begin" & NL & + " \o0 <= \i0;" & NL, + Inst); + if Id = Id_Dyn_Insert_En then + -- TODO: fix indentation. + Disp_Template (" if \i3 = '1' then" & NL, Inst); + end if; + Disp_Template + (" \o0 (", Inst); + if Iw > 1 then + Disp_Template + ("to_integer (\ui2) + (\sp0 + \n0)" & NL & + " downto ", + Inst, (0 => Iw - 1)); + end if; + Disp_Template + ("to_integer (\ui2) + (\sp0))" & + " <= \i1;" & NL, + Inst); + if Id = Id_Dyn_Insert_En then + Disp_Template (" end if;" & NL, Inst); + end if; + Disp_Template + (" end process;" & NL, + Inst); + end; + when Id_Const_UB32 + | Id_Const_UL32 + | Id_Const_Z + | Id_Const_X => + Disp_Template (" \o0 <= ", Inst); + Disp_Constant_Inline (Inst); + Put_Line (";"); + when Id_Const_Bit => + null; + when Id_Adff + | Id_Iadff => + Disp_Template (" always @(\ei0 or posedge \i2)" & NL & + " if (\i2)" & NL & + " \o0 <= \i3;" & NL & + " else" & NL & + " \o0 <= \i1;" & NL, Inst); + when Id_Dff + | Id_Idff => + Disp_Template (" always @(\ei0)" & NL & + " \o0 <= \i1;" & NL, Inst); + when Id_Mux2 => + Disp_Template (" assign \o0 = \i0 ? \i2 : \i1;" & NL, Inst); + when Id_Mux4 => + Disp_Template (" always @*" & NL & + " case (\i0)" & NL & + " 2'b00: \o0 <= \i1;" & NL & + " 2'b01: \o0 <= \i2;" & NL & + " 2'b10: \o0 <= \i3;" & NL & + " 2'b11: \o0 <= \i4;" & NL & + " endcase" & NL, Inst); + when Id_Pmux => + Disp_Pmux (Inst); + when Id_Add => + Disp_Template (" assign \o0 = \i0 + \i1;" & NL, Inst); + when Id_Sub => + Disp_Template (" assign \o0 = \i0 - \i1;" & NL, Inst); + when Id_Umin => + Disp_Template (" assign \o0 = (\i0 < \i1) ? \i0 : \i1;" & NL, + Inst); + when Id_Smin => + Disp_Template (" \o0 <= \i0 when \si0 < \si1 else \i1;" & NL, + Inst); + when Id_Umax => + Disp_Template (" assign \o0 = (\i0 > \i1) ? \i0 : \i1;" & NL, + Inst); + when Id_Smax => + Disp_Template (" \o0 <= \i0 when \si0 > \si1 else \i1;" & NL, + Inst); + when Id_Umul => + Disp_Template (" assign \o0 = \i0 * \i1; // umul" & NL, Inst); + when Id_Smul => + Disp_Template (" assign \o0 = \i0 * \i1; // smul" & NL, Inst); + when Id_Smod => + Disp_Template (" assign \o0 = \i0 % \i1; // smod" & NL, Inst); + when Id_Srem => + Disp_Template (" assign \o0 = \i0 % \i1; // srem" & NL, Inst); + when Id_Umod => + Disp_Template (" assign \o0 = \i0 % \i1; // umod" & NL, Inst); + when Id_Sdiv => + Disp_Template (" assign \o0 = \i0 / \i1; // sdiv" & NL, Inst); + when Id_Udiv => + Disp_Template (" assign \o0 = \i0 / \i1; // udiv" & NL, Inst); + when Id_Lsl => + Disp_Template (" assign \o0 = \i0 << \i1;" & NL, Inst); + when Id_Lsr => + Disp_Template (" assign \o0 = \i0 >> \i1;" & NL, Inst); + when Id_Asr => + Disp_Template (" assign \o0 = \si0 >> \i1;" & NL, Inst); + when Id_Rol => + Disp_Template + (" \o0 <= std_logic_vector " + & "(rotate_left (\ui0, to_integer (\ui1)));" & NL, Inst); + + when Id_Ult => + Disp_Template (" assign \o0 = \ui0 < \ui1;" & NL, Inst); + when Id_Ule => + Disp_Template (" assign \o0 = \ui0 <= \ui1;" & NL, Inst); + when Id_Ugt => + Disp_Template (" assign \o0 = \ui0 > \ui1;" & NL, Inst); + when Id_Uge => + Disp_Template (" assign \o0 = \ui0 >= \ui1;" & NL, Inst); + when Id_Slt => + Disp_Template (" assign \o0 = \si0 < \si1;" & NL, Inst); + when Id_Sle => + Disp_Template (" assign \o0 = \si0 <= \si1;" & NL, Inst); + when Id_Sgt => + Disp_Template (" assign \o0 = \si0 > \si1;" & NL, Inst); + when Id_Sge => + Disp_Template (" assign \o0 = \si0 >= \si1;" & NL, Inst); + when Id_Eq => + Disp_Template (" assign \o0 = \i0 == \i1;" & NL, Inst); + when Id_Ne => + Disp_Template (" assign \o0 = \i0 != \i1;" & NL, Inst); + when Id_Or => + Disp_Template (" assign \o0 = \i0 | \i1;" & NL, Inst); + when Id_And => + Disp_Template (" assign \o0 = \i0 & \i1;" & NL, Inst); + when Id_Xor => + Disp_Template (" assign \o0 = \i0 ^ \i1;" & NL, Inst); + when Id_Nor => + Disp_Template (" assign \o0 = ~(\i0 | \i1);" & NL, Inst); + when Id_Nand => + Disp_Template (" assign \o0 = ~(\i0 & \i1);" & NL, Inst); + when Id_Xnor => + Disp_Template (" assign \o0 = ~(\i0 ^ \i1);" & NL, Inst); + + when Id_Concat2 => + Disp_Template (" assign \o0 = {\i0, \i1};" & NL, Inst); + when Id_Concat3 => + Disp_Template (" assign \o0 = {\i0, \i1, \i2};" & NL, Inst); + when Id_Concat4 => + Disp_Template (" assign \o0 = {\i0, \i1, \i2, \i3};" & NL, Inst); + when Id_Concatn => + Disp_Template (" assign \o0 = {\i0", Inst); + for I in 1 .. Get_Nbr_Inputs (Inst) - 1 loop + Disp_Template (", ", Inst); + Disp_Net_Expr (Get_Input_Net (Inst, I), Inst, Conv_None); + end loop; + Disp_Template("};" & NL, Inst); + when Id_Utrunc + | Id_Strunc => + declare + W : constant Width := Get_Width (Get_Output (Inst, 0)); + begin + if W = 0 then + -- Do not try to slice the input, as it can be a single + -- wire. + Disp_Template (" assign \o0 = """"", Inst); + else + Disp_Template (" assign \o0 = \i0", Inst); + if W = 1 then + Disp_Template ("[0]", Inst); + else + Disp_Template ("[\n0:0]", Inst, (0 => W - 1)); + end if; + end if; + Disp_Template ("; // trunc" & NL, Inst); + end; + when Id_Uextend => + declare + Ow : constant Width := Get_Width (Get_Output (Inst, 0)); + Iw : constant Width := Get_Width (Get_Input_Net (Inst, 0)); + begin + pragma Assert (Ow > Iw); + Disp_Template (" assign \o0 = {", Inst); + Put_Uns32 (Ow - Iw); + Disp_Template ("'b0, \i0}; // uext" & NL, Inst); + end; + when Id_Sextend => + declare + Ow : constant Width := Get_Width (Get_Output (Inst, 0)); + Iw : constant Width := Get_Width (Get_Input_Net (Inst, 0)); + begin + pragma Assert (Ow > Iw); + Disp_Template (" assign \o0 = {{\n0{\i0", + Inst, (0 => Ow - Iw)); + if Iw > 1 then + Disp_Template ("[\n0]", Inst, (0 => Iw - 1)); + end if; + Disp_Template ("}}, \i0}; // sext" & NL, Inst); + end; + when Id_Red_Or => + Disp_Template (" assign \o0 = |(\i0);" & NL, Inst); + when Id_Red_And => + Disp_Template (" assign \o0 = &(\i0);" & NL, Inst); + when Id_Red_Xor => + Disp_Template (" assign \o0 = ^(\i0);" & NL, Inst); + + when Id_Posedge => + Disp_Template (" assign \o0 = 1'b0; // posedge" & NL, Inst); + when Id_Negedge => + Disp_Template (" assign \o0 = 1'b0; // negedge" & NL, Inst); + when Id_Tri => + Disp_Template (" assign \o0 = \i0 ? \i1 : \n0'bz;" & NL, + Inst, (0 => Get_Width (Get_Output (Inst, 0)))); + when Id_Assert => + Disp_Template + (" always @*" & NL & + " if (!\i0)" & NL & + " $fatal(1, ""assertion failure \l0"");" & NL, Inst); + when Id_Assume => + Disp_Template + (" \l0: assert \i0 = '1' severity warning; -- assume" & NL, + Inst); + when Id_Cover => + Disp_Template + (" \l0: assert \i0 = '1' severity note; -- cover" & NL, + Inst); + when Id_Assert_Cover => + Disp_Template + (" always @*" & NL & + " if (!\i0)" & NL & + " $fatal(1, ""assertion(cover) failure \l0"");" & NL, + Inst); + when Id_Resolver => + Disp_Template + (" assign \o0 = \i0;" & NL, Inst); + Disp_Template + (" assign \o0 = \i1;" & NL, Inst); + when others => + Disp_Instance_Gate (Inst); + end case; + end Disp_Instance_Inline; + + procedure Disp_Module_Declarations (M : Module) + is + Id : Module_Id; + begin + -- Display signal declarations. + -- There are as many signals as gate outputs. + for Inst of Instances (M) loop + Id := Get_Id (Inst); + case Id is + when Id_Memory + | Id_Memory_Init => + -- For memories: skip the chain. + null; + when Id_Mem_Wr_Sync => + -- For memories: skip the chain. + null; + when Id_Mem_Rd + | Id_Mem_Rd_Sync => + -- For memories: skip the chain. + declare + N : constant Net := Get_Output (Inst, 1); + begin + if Id = Id_Mem_Rd_Sync then + Put (" reg "); + else + Put (" wire "); + end if; + Put_Type (Get_Width (N)); + Disp_Net_Name (N); + Put_Line ("; // mem_rd"); + end; + when others => + if not Is_Self_Instance (Inst) + and then not (Flag_Merge_Lit + and then Id in Constant_Module_Id + and then Id < Id_User_None + and then not Need_Signal (Inst)) + and then not (Flag_Merge_Edge + and then Id in Edge_Module_Id + and then not Need_Edge (Inst)) + then + if Locations.Get_Location (Inst) = No_Location then + case Id is + when Id_Const_UB32 + | Id_Const_SB32 + | Id_Const_UL32 + | Id_Const_Bit + | Id_Const_Log + | Id_Const_Z + | Id_Const_X + | Id_Const_0 + | Id_Concat2 + | Id_Concat3 + | Id_Concat4 + | Id_Concatn + | Id_Extract => + null; + when others => + raise Internal_Error; + end case; + end if; + for N of Outputs (Inst) loop + case Id is + when Id_Dff + | Id_Idff + | Id_Adff + | Id_Iadff => + -- As expected + Put (" reg "); + when Id_Mux4 + | Id_Pmux => + -- Implemented by a process + Put (" reg "); + when others => + Put (" wire "); + end case; + Put_Type (Get_Width (N)); + Disp_Net_Name (N); + Put_Line (";"); + end loop; + end if; + end case; + end loop; + end Disp_Module_Declarations; + + procedure Disp_Module_Statements (M : Module) + is + Self_Inst : constant Instance := Get_Self_Instance (M); + begin + -- Output assignments. + declare + Idx : Port_Idx; + begin + Idx := 0; + for I of Inputs (Self_Inst) loop + Put (" assign "); + Put_Name (Get_Output_Desc (M, Idx).Name); + Put (" = "); + Disp_Net_Name (Get_Driver (I)); + Put_Line (";"); + Idx := Idx + 1; + end loop; + end; + + for Inst of Instances (M) loop + case Get_Id (Inst) is + when Constant_Module_Id => + if not Flag_Merge_Lit then + Disp_Instance_Inline (Inst); + end if; + when Edge_Module_Id => + if (not Flag_Merge_Edge) or else Need_Edge (Inst) then + Disp_Instance_Inline (Inst); + end if; + when others => + Disp_Instance_Inline (Inst); + end case; + end loop; + end Disp_Module_Statements; + + procedure Disp_Module_Port + (Desc : Port_Desc; Dir : Port_Kind; First : in out Boolean) is + begin + if First then + Put (" ("); + First := False; + else + Put_Line (","); + Put (" "); + end if; + case Dir is + when Port_In => + Put ("input "); + when Port_Out => + Put ("output "); + when Port_Inout => + Put ("inout "); + end case; + Put_Type (Desc.W); + Put_Name (Desc.Name); + end Disp_Module_Port; + + procedure Disp_Module_Ports (M : Module) + is + First : Boolean; + Desc : Port_Desc; + begin + First := True; + for I in 1 .. Get_Nbr_Inputs (M) loop + Disp_Module_Port (Get_Input_Desc (M, I - 1), Port_In, First); + end loop; + for I in 1 .. Get_Nbr_Outputs (M) loop + Desc := Get_Output_Desc (M, I - 1); + if Desc.Is_Inout then + Disp_Module_Port (Desc, Port_Inout, First); + else + Disp_Module_Port (Desc, Port_Out, First); + end if; + end loop; + if not First then + Put (")"); + end if; + Put_Line (";"); + end Disp_Module_Ports; + + procedure Disp_Module_Parameters (M : Module) + is + Nbr : constant Param_Nbr := Get_Nbr_Params (M); + Desc : Param_Desc; + begin + if Nbr = 0 then + return; + end if; + for I in 1 .. Nbr loop + if I = 1 then + Put_Line (" #("); + else + Put_Line (","); + end if; + Desc := Get_Param_Desc (M, I - 1); + Put (" "); + Put_Name (Desc.Name); + end loop; + Put_Line (")"); + end Disp_Module_Parameters; + + procedure Disp_Verilog_Module (M : Module) + is + Self_Inst : constant Instance := Get_Self_Instance (M); + begin + -- Module id and name. + Put ("module "); + Put_Name (Get_Module_Name (M)); + New_Line; + + Disp_Module_Parameters (M); + + Disp_Module_Ports (M); + + if Self_Inst /= No_Instance then + Disp_Module_Declarations (M); + Disp_Module_Statements (M); + end if; + Put_Line ("endmodule"); + New_Line; + end Disp_Verilog_Module; + + procedure Disp_Verilog (M : Module; Is_Top : Boolean) is + begin + -- Disp in reverse order. + declare + Num : Natural; + begin + Num := 0; + for S of Sub_Modules (M) loop + if Get_Id (S) >= Id_User_None then + Num := Num + 1; + end if; + end loop; + + declare + type Module_Array is array (1 .. Num) of Module; + Modules : Module_Array; + begin + Num := 0; + for S of Sub_Modules (M) loop + if Get_Id (S) >= Id_User_None then + Num := Num + 1; + Modules (Num) := S; + end if; + end loop; + + for I in reverse Modules'Range loop + Disp_Verilog (Modules (I), False); + end loop; + end; + end; + + if not Is_Top then + Disp_Verilog_Module (M); + end if; + end Disp_Verilog; + + procedure Disp_Verilog (M : Module) is + begin + Disp_Verilog (M, True); + end Disp_Verilog; +end Netlists.Disp_Verilog; diff --git a/src/synth/netlists-disp_verilog.ads b/src/synth/netlists-disp_verilog.ads new file mode 100644 index 000000000..0873c0eb9 --- /dev/null +++ b/src/synth/netlists-disp_verilog.ads @@ -0,0 +1,21 @@ +-- Disp a netlist in verilog. +-- Copyright (C) 2021 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>. + +package Netlists.Disp_Verilog is + procedure Disp_Verilog (M : Module); +end Netlists.Disp_Verilog; |