1--  GHDL driver for synthesis
2--  Copyright (C) 2016 Tristan Gingold
3--
4--  This program is free software: you can redistribute it and/or modify
5--  it under the terms of the GNU General Public License as published by
6--  the Free Software Foundation, either version 2 of the License, or
7--  (at your option) any later version.
8--
9--  This program is distributed in the hope that it will be useful,
10--  but WITHOUT ANY WARRANTY; without even the implied warranty of
11--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12--  GNU General Public License for more details.
13--
14--  You should have received a copy of the GNU General Public License
15--  along with this program.  If not, see <gnu.org/licenses>.
16
17with GNAT.OS_Lib; use GNAT.OS_Lib;
18
19with Types; use Types;
20with Name_Table;
21with Files_Map;
22with Ghdllocal; use Ghdllocal;
23with Ghdlcomp; use Ghdlcomp;
24with Ghdlmain; use Ghdlmain;
25with Options; use Options;
26with Errorout;
27with Errorout.Console;
28with Version;
29with Default_Paths;
30with Bug;
31with Simple_IO;
32
33with Libraries;
34with Flags;
35with Vhdl.Nodes; use Vhdl.Nodes;
36with Vhdl.Errors;
37with Vhdl.Scanner;
38with Vhdl.Std_Package;
39with Vhdl.Canon;
40with Vhdl.Configuration;
41with Vhdl.Annotations;
42with Vhdl.Utils;
43
44with Netlists.Dump;
45with Netlists.Disp_Vhdl;
46with Netlists.Disp_Dot;
47with Netlists.Errors;
48
49with Synthesis;
50with Synth.Disp_Vhdl;
51with Synth.Context; use Synth.Context;
52with Synth.Flags; use Synth.Flags;
53
54package body Ghdlsynth is
55   type Out_Format is
56     (Format_Default,
57      Format_Raw, Format_Dump, Format_Dot,
58      Format_Vhdl,
59      Format_None);
60
61   type Name_Id_Array is array (Natural range <>) of Name_Id;
62
63   --  Command --synth
64   type Command_Synth is new Command_Lib with record
65      --  Control format of the output.
66      Disp_Inline : Boolean := True;
67      Disp_Id : Boolean := True;
68      Oformat     : Out_Format := Format_Default;
69
70      Flag_Stats : Boolean := False;
71
72      --  Control name encoding of the top-entity.
73      Top_Encoding : Name_Encoding := Name_Asis;
74
75      --  If True, a failure is expected.  For tests.
76      Expect_Failure : Boolean := False;
77
78      Nbr_Vendor_Libraries : Natural := 0;
79      Vendor_Libraries : Name_Id_Array (1 .. 8) := (others => No_Name_Id);
80   end record;
81   function Decode_Command (Cmd : Command_Synth; Name : String)
82                           return Boolean;
83   function Get_Short_Help (Cmd : Command_Synth) return String;
84   procedure Disp_Long_Help (Cmd : Command_Synth);
85   procedure Decode_Option (Cmd : in out Command_Synth;
86                            Option : String;
87                            Arg : String;
88                            Res : out Option_State);
89   procedure Perform_Action (Cmd : in out Command_Synth;
90                             Args : Argument_List);
91
92   function Decode_Command (Cmd : Command_Synth; Name : String)
93                           return Boolean
94   is
95      pragma Unreferenced (Cmd);
96   begin
97      return Name = "synth"
98        or else Name = "--synth";
99   end Decode_Command;
100
101   function Get_Short_Help (Cmd : Command_Synth) return String
102   is
103      pragma Unreferenced (Cmd);
104   begin
105      return "synth [FILES... -e] UNIT [ARCH]"
106        & ASCII.LF & "  Synthesis from UNIT"
107        & ASCII.LF & "  alias: --synth";
108   end Get_Short_Help;
109
110   procedure Disp_Long_Help (Cmd : Command_Synth)
111   is
112      pragma Unreferenced (Cmd);
113      procedure P (Str : String) renames Simple_IO.Put_Line;
114   begin
115      P ("You can directly pass the list of files to synthesize:");
116      P ("   --synth [OPTIONS] { [--work=NAME] FILE } -e [UNIT]");
117      P (" If UNIT is not present, the top unit is automatically found");
118      P (" You can use --work=NAME to change the library between files");
119      P ("Or use already analysed files:");
120      P ("   --synth [OPTIONS] -e UNIT");
121      P ("In addition to analyze options, you can use:");
122      P ("  -gNAME=VALUE");
123      P ("    Override the generic NAME of the top unit");
124      P ("  --vendor-library=NAME");
125      P ("    Any unit from library NAME is a black boxe");
126      P ("  --no-formal");
127      P ("    Neither synthesize assert nor PSL");
128      P ("  --no-assert-cover");
129      P ("    Cover PSL assertion activation");
130      P ("  --assert-assumes");
131      P ("    Treat all PSL asserts like PSL assumes");
132      P ("  --assume-asserts");
133      P ("    Treat all PSL assumes like PSL asserts");
134   end Disp_Long_Help;
135
136   procedure Decode_Option (Cmd : in out Command_Synth;
137                            Option : String;
138                            Arg : String;
139                            Res : out Option_State)
140   is
141      pragma Assert (Option'First = 1);
142   begin
143      Res := Option_Ok;
144
145      if Option'Last > 3
146        and then Option (2) = 'g'
147        and then Is_Generic_Override_Option (Option)
148      then
149         Res := Decode_Generic_Override_Option (Option);
150      elsif Option = "--no-formal" then
151         Synth.Flags.Flag_Formal := False;
152      elsif Option = "--formal" then
153         Synth.Flags.Flag_Formal := True;
154      elsif Option = "--no-assert-cover" then
155         Synth.Flags.Flag_Assert_Cover := False;
156      elsif Option = "--assert-cover" then
157         Synth.Flags.Flag_Assert_Cover := True;
158      elsif Option = "--assert-assumes" then
159         Synth.Flags.Flag_Assert_As_Assume := True;
160      elsif Option = "--assume-asserts" then
161         Synth.Flags.Flag_Assume_As_Assert := True;
162      elsif Option = "--top-name=hash" then
163         Cmd.Top_Encoding := Name_Hash;
164      elsif Option = "--top-name=asis" then
165         Cmd.Top_Encoding := Name_Asis;
166      elsif Option'Last > 17
167        and then Option (1 .. 17) = "--vendor-library="
168      then
169         if Cmd.Nbr_Vendor_Libraries >= Cmd.Vendor_Libraries'Last then
170            --  FIXME: use a table/vector ?
171            Errorout.Error_Msg_Option ("too many vendor libraries");
172            Res := Option_Err;
173         else
174            declare
175               Name : String := Option (18 .. Option'Last);
176               Err : Boolean;
177            begin
178               Vhdl.Scanner.Convert_Identifier (Name, Err);
179               if Err then
180                  Res := Option_Err;
181               else
182                  Cmd.Nbr_Vendor_Libraries := Cmd.Nbr_Vendor_Libraries + 1;
183                  Cmd.Vendor_Libraries (Cmd.Nbr_Vendor_Libraries) :=
184                    Name_Table.Get_Identifier (Name);
185               end if;
186            end;
187         end if;
188      elsif Option = "--expect-failure" then
189         Cmd.Expect_Failure := True;
190      elsif Option = "--disp-noinline" then
191         Cmd.Disp_Inline := False;
192      elsif Option = "--disp-noid" then
193         Cmd.Disp_Id := False;
194      elsif Option = "--out=raw" then
195         Cmd.Oformat := Format_Raw;
196      elsif Option = "--out=dump" then
197         Cmd.Oformat := Format_Dump;
198      elsif Option = "--out=dot" then
199         Cmd.Oformat := Format_Dot;
200      elsif Option = "--out=none" then
201         Cmd.Oformat := Format_None;
202      elsif Option = "--out=vhdl" then
203         Cmd.Oformat := Format_Vhdl;
204      elsif Option = "-di" then
205         Flag_Debug_Noinference := True;
206      elsif Option = "-dc" then
207         Flag_Debug_Nocleanup := True;
208      elsif Option = "-dm" then
209         Flag_Debug_Nomemory1 := True;
210         Flag_Debug_Nomemory2 := True;
211      elsif Option = "-dm2" then
212         --  Reduce muxes, but do not create memories.
213         Flag_Debug_Nomemory2 := True;
214      elsif Option = "-de" then
215         Flag_Debug_Noexpand := True;
216      elsif Option = "-t" then
217         Flag_Trace_Statements := True;
218      elsif Option = "-i" then
219         Flag_Debug_Init := True;
220      elsif Option = "-g" then
221         Flag_Debug_Enable := True;
222      elsif Option = "-v" then
223         if not Synth.Flags.Flag_Verbose then
224            Synth.Flags.Flag_Verbose := True;
225         else
226            Flags.Verbose := True;
227         end if;
228      elsif Option = "--stats" then
229         Cmd.Flag_Stats := True;
230      else
231         Decode_Option (Command_Lib (Cmd), Option, Arg, Res);
232      end if;
233   end Decode_Option;
234
235   --  Init, analyze and configure.
236   --  Return the top configuration.
237   function Ghdl_Synth_Configure
238     (Init : Boolean; Cmd : Command_Synth; Args : Argument_List) return Node
239   is
240      use Vhdl.Errors;
241      use Vhdl.Configuration;
242      use Errorout;
243      E_Opt : Integer;
244      Opt_Arg : Natural;
245      Design_File : Iir;
246      Config : Iir;
247      Top : Iir;
248      Prim_Id : Name_Id;
249      Sec_Id : Name_Id;
250   begin
251      --  If the '-e' switch is present, there is a list of files.
252      E_Opt := Args'First - 1;
253      for I in Args'Range loop
254         if Args (I).all = "-e" then
255            E_Opt := I;
256            exit;
257         end if;
258      end loop;
259
260      if Init then
261         Vhdl.Annotations.Flag_Synthesis := True;
262         Vhdl.Scanner.Flag_Comment_Keyword := True;
263         Vhdl.Scanner.Flag_Pragma_Comment := True;
264
265         Common_Compile_Init (False);
266         --  Will elaborate.
267         Flags.Flag_Elaborate := True;
268
269         --  Load content only if there are no files.
270         Libraries.Load_Work_Library (E_Opt >= Args'First);
271
272         --  Do not canon concurrent statements.
273         Vhdl.Canon.Canon_Flag_Concurrent_Stmts := False;
274
275         --  Do not create concurrent signal assignment for inertial
276         --  association.  They are handled directly.
277         Vhdl.Canon.Canon_Flag_Inertial_Associations := False;
278      end if;
279
280      --  Mark vendor libraries.
281      for I in 1 .. Cmd.Nbr_Vendor_Libraries loop
282         declare
283            Lib : Node;
284         begin
285            Lib := Libraries.Get_Library
286              (Cmd.Vendor_Libraries (I), No_Location);
287            Set_Vendor_Library_Flag (Lib, True);
288         end;
289      end loop;
290
291      Flags.Flag_Elaborate_With_Outdated := E_Opt >= Args'First;
292
293      --  Analyze files (if any)
294      for I in Args'First .. E_Opt - 1 loop
295         declare
296            Arg : String renames Args (I).all;
297            pragma Assert (Arg'First = 1);
298            Id : Name_Id;
299         begin
300            if Arg'Last > 7 and then Arg (1 .. 7) = "--work=" then
301               Id := Libraries.Decode_Work_Option (Arg);
302               if Id = Null_Identifier then
303                  return Null_Iir;
304               end if;
305               Libraries.Work_Library_Name := Id;
306               Libraries.Load_Work_Library (True);
307            else
308               if Files_Map.Find_Language (Arg) /= Language_Vhdl then
309                  Errorout.Report_Msg
310                    (Warnid_Library, Option, No_Source_Coord,
311                     "unexpected extension for vhdl file %i",
312                     (1 => +Name_Table.Get_Identifier (Arg)));
313               end if;
314
315               Ghdlcomp.Compile_Load_File (Arg);
316            end if;
317         end;
318      end loop;
319      pragma Unreferenced (Design_File);
320
321      if Nbr_Errors > 0 then
322         --  No need to configure if there are missing units.
323         return Null_Iir;
324      end if;
325
326      --  Elaborate
327      if E_Opt = Args'Last then
328         --  No unit.
329         Top := Vhdl.Configuration.Find_Top_Entity
330           (Libraries.Work_Library, Libraries.Command_Line_Location);
331         if Top = Null_Node then
332            Ghdlmain.Error ("no top unit found");
333            return Null_Iir;
334         end if;
335         Errorout.Report_Msg (Msgid_Note, Option, No_Source_Coord,
336                              "top entity is %i", (1 => +Top));
337         if Nbr_Errors > 0 then
338            --  No need to configure if there are missing units.
339            return Null_Iir;
340         end if;
341         Prim_Id := Get_Identifier (Top);
342         Sec_Id := Null_Identifier;
343      else
344         Extract_Elab_Unit ("--synth", Args (E_Opt + 1 .. Args'Last), Opt_Arg,
345                            Prim_Id, Sec_Id);
346         if Opt_Arg <= Args'Last then
347            Ghdlmain.Error ("extra options ignored");
348            return Null_Iir;
349         end if;
350      end if;
351
352      Config := Vhdl.Configuration.Configure (Prim_Id, Sec_Id);
353
354      if Nbr_Errors > 0 then
355         --  No need to configure if there are missing units.
356         return Null_Iir;
357      end if;
358
359      Vhdl.Configuration.Add_Verification_Units;
360
361      --  Check (and possibly abandon) if entity can be at the top of the
362      --  hierarchy.
363      declare
364         Entity : constant Iir :=
365           Vhdl.Utils.Get_Entity_From_Configuration (Config);
366      begin
367         Vhdl.Configuration.Apply_Generic_Override (Entity);
368         Vhdl.Configuration.Check_Entity_Declaration_Top (Entity, False);
369         if Nbr_Errors > 0 then
370            return Null_Iir;
371         end if;
372      end;
373
374      --  Annotate all units.
375      Vhdl.Annotations.Initialize_Annotate;
376      Vhdl.Annotations.Annotate (Vhdl.Std_Package.Std_Standard_Unit);
377      for I in Design_Units.First .. Design_Units.Last loop
378         Vhdl.Annotations.Annotate (Design_Units.Table (I));
379      end loop;
380
381      return Config;
382   end Ghdl_Synth_Configure;
383
384   procedure Disp_Design (Cmd : Command_Synth;
385                          Default : Out_Format;
386                          Res : Module;
387                          Config : Iir;
388                          Inst : Synth_Instance_Acc)
389   is
390      Format : Out_Format;
391      Ent : Iir;
392   begin
393      Format := Cmd.Oformat;
394      if Format = Format_Default then
395         Format := Default;
396      end if;
397
398      case Format is
399         when Format_Default =>
400            raise Internal_Error;
401         when Format_None =>
402            null;
403         when Format_Raw =>
404            Netlists.Dump.Flag_Disp_Inline := Cmd.Disp_Inline;
405            Netlists.Dump.Flag_Disp_Id := Cmd.Disp_Id;
406            Netlists.Dump.Disp_Module (Res);
407         when Format_Dump =>
408            Netlists.Dump.Flag_Disp_Inline := Cmd.Disp_Inline;
409            Netlists.Dump.Dump_Module (Res);
410         when Format_Dot =>
411            Netlists.Disp_Dot.Disp_Dot_Top_Module (Res);
412         when Format_Vhdl =>
413            if Boolean'(True) then
414               Ent := Vhdl.Utils.Get_Entity_From_Configuration (Config);
415               Synth.Disp_Vhdl.Disp_Vhdl_Wrapper (Ent, Res, Inst);
416            else
417               Netlists.Disp_Vhdl.Disp_Vhdl (Res);
418            end if;
419      end case;
420   end Disp_Design;
421
422   function Ghdl_Synth
423     (Init : Natural; Argc : Natural; Argv : C_String_Array_Acc)
424     return Module
425   is
426      use Vhdl.Configuration;
427      Args : Argument_List (1 .. Argc);
428      Res : Module;
429      Cmd : Command_Synth;
430      First_Arg : Natural;
431      Config : Node;
432      Inst : Synth_Instance_Acc;
433   begin
434      --  Create arguments list.
435      for I in 0 .. Argc - 1 loop
436         declare
437            Arg : constant Ghdl_C_String := Argv (I);
438         begin
439            Args (I + 1) := new String'(Arg (1 .. strlen (Arg)));
440         end;
441      end loop;
442
443      --  Forget any previous errors.
444      Errorout.Nbr_Errors := 0;
445
446      --  Find the command.  This is a little bit convoluted...
447      Decode_Command_Options (Cmd, Args, First_Arg);
448
449      --  Do the real work!
450      Config := Ghdl_Synth_Configure
451        (Init /= 0, Cmd, Args (First_Arg .. Args'Last));
452      if Config = Null_Iir then
453         return No_Module;
454      end if;
455
456      Synthesis.Synth_Design (Config, Cmd.Top_Encoding, Res, Inst);
457      if Res = No_Module then
458         return No_Module;
459      end if;
460
461      Disp_Design (Cmd, Format_None, Res, Config, Inst);
462
463      --  De-elaborate all packages, so that they could be re-used for
464      --  synthesis of a second design.
465      --  FIXME: move to vhdl.configure ?
466      for I in Design_Units.First .. Design_Units.Last loop
467         Set_Elab_Flag (Design_Units.Table (I), False);
468      end loop;
469      Set_Elab_Flag (Vhdl.Std_Package.Std_Standard_Unit, False);
470
471      Vhdl.Annotations.Finalize_Annotate;
472      Synth.Context.Free_Base_Instance;
473      return Res;
474
475   exception
476      when Option_Error
477        | Errorout.Compilation_Error =>
478         return No_Module;
479      when E: others =>
480         --  Avoid possible issues with exceptions...
481         Bug.Disp_Bug_Box (E);
482         return No_Module;
483   end Ghdl_Synth;
484
485   procedure Perform_Action (Cmd : in out Command_Synth;
486                             Args : Argument_List)
487   is
488      Res : Module;
489      Inst : Synth_Instance_Acc;
490      Config : Iir;
491   begin
492      Config := Ghdl_Synth_Configure (True, Cmd, Args);
493
494      if Config = Null_Iir then
495         if Cmd.Expect_Failure then
496            return;
497         else
498            raise Errorout.Compilation_Error;
499         end if;
500      end if;
501
502      Netlists.Errors.Initialize;
503
504      Synthesis.Synth_Design (Config, Cmd.Top_Encoding, Res, Inst);
505      if Res = No_Module then
506         if Cmd.Expect_Failure then
507            return;
508         else
509            raise Errorout.Compilation_Error;
510         end if;
511      elsif Cmd.Expect_Failure then
512         raise Errorout.Compilation_Error;
513      end if;
514
515      Disp_Design (Cmd, Format_Vhdl, Res, Config, Inst);
516
517      if Cmd.Flag_Stats then
518         Netlists.Disp_Stats;
519      end if;
520   end Perform_Action;
521
522   function Get_Libghdl_Name return String
523   is
524      Libghdl_Version : String := Version.Ghdl_Ver;
525   begin
526      for I in Libghdl_Version'Range loop
527         if Libghdl_Version (I) = '.' or Libghdl_Version (I) = '-' then
528            Libghdl_Version (I) := '_';
529         end if;
530      end loop;
531      return "libghdl-" & Libghdl_Version
532        & Default_Paths.Shared_Library_Extension;
533   end Get_Libghdl_Name;
534
535   function Get_Libghdl_Path return String is
536   begin
537      if Ghdllocal.Exec_Prefix = null then
538         --  Compute install path (only once).
539         Ghdllocal.Set_Exec_Prefix_From_Program_Name;
540      end if;
541
542      return Ghdllocal.Exec_Prefix.all & Directory_Separator & "lib"
543        & Directory_Separator & Get_Libghdl_Name;
544   end Get_Libghdl_Path;
545
546   function Get_Libghdl_Include_Dir return String is
547   begin
548      --  Compute install path
549      Ghdllocal.Set_Exec_Prefix_From_Program_Name;
550
551      return Ghdllocal.Exec_Prefix.all & Directory_Separator & "include";
552   end Get_Libghdl_Include_Dir;
553
554   procedure Register_Commands is
555   begin
556      Ghdlmain.Register_Command (new Command_Synth);
557      Register_Command
558        (new Command_Str_Disp'
559           (Command_Type with
560            Cmd_Str => new String'
561              ("--libghdl-name"),
562            Help_Str => new String'
563              ("--libghdl-name"
564              & ASCII.LF & "  Display libghdl name"),
565            Disp => Get_Libghdl_Name'Access));
566      Register_Command
567        (new Command_Str_Disp'
568           (Command_Type with
569            Cmd_Str => new String'
570              ("--libghdl-library-path"),
571            Help_Str => new String'
572              ("--libghdl-library-path"
573              & ASCII.LF & "  Display libghdl library path"),
574            Disp => Get_Libghdl_Path'Access));
575      Register_Command
576        (new Command_Str_Disp'
577           (Command_Type with
578            Cmd_Str => new String'
579              ("--libghdl-include-dir"),
580            Help_Str => new String'
581              ("--libghdl-include-dir"
582              & ASCII.LF & "  Display libghdl include directory"),
583            Disp => Get_Libghdl_Include_Dir'Access));
584   end Register_Commands;
585
586   procedure Init_For_Ghdl_Synth is
587   begin
588      Ghdlsynth.Register_Commands;
589      Errorout.Console.Install_Handler;
590      Options.Initialize;
591      Netlists.Errors.Initialize;
592   end Init_For_Ghdl_Synth;
593end Ghdlsynth;
594