package hardcaml

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file graph.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
open! Import
open Printf
open Signal

(* write a DOT file with rank information - looks absolutely terrible *)
let write_dot_rank chan circuit =
  (* {[
       let get_name signal =
         (* {[
              match names signal with
              | [] -> "_" ^ Int64.to_string (uid signal)
              | h :: t -> h
            ]} *)
         Int64.to_string (uid signal)
       in
     ]} *)
  let outputs =
    List.fold
      (Circuit.outputs circuit)
      ~init:(Set.empty (module Uid))
      ~f:(fun set signal -> Set.add set (uid signal))
  in
  (* create a suitable fan-out mapping *)
  let fdeps s =
    match s with
    | Mem _ -> [ List.hd_exn (deps s) ]
    | Reg _ -> [ List.hd_exn (deps s) ]
    | _ -> deps s
  in
  let fan_out = Signal_graph.fan_out_map ~deps:fdeps (Circuit.signal_graph circuit) in
  let find_fan_out signal =
    match Map.find fan_out signal with
    | None -> []
    | Some s -> Set.to_list s
  in
  let uids l = List.map l ~f:uid in
  (* We start at the inputs, and traverse forward to the outputs, effectively using depth
     first search. *)
  let rec dfs visited ranks signals =
    match signals with
    | [] -> ranks
    | _ ->
      (* add these signals to the visited set *)
      let visited =
        List.fold signals ~init:visited ~f:(fun set signal -> Set.add set signal)
      in
      (* find fan_out from this level *)
      let signals =
        List.concat (List.map signals ~f:(fun signal -> find_fan_out signal))
      in
      (* filter out already visited signals, and outputs *)
      let signals =
        List.filter signals ~f:(fun signal ->
          (not (Set.mem visited signal)) && not (Set.mem outputs signal))
      in
      (* create set of uids of nodes at this rank *)
      let rank =
        List.fold
          signals
          ~init:(Set.empty (module Uid))
          ~f:(fun set signal -> Set.add set signal)
      in
      if Set.is_empty rank then ranks else dfs visited (rank :: ranks) signals
  in
  let ranks =
    dfs (Set.empty (module Uid)) [] (Circuit.inputs circuit |> uids)
    |> List.map ~f:Set.to_list
  in
  (* create the output level and add it to the ranks *)
  let ranks =
    List.map (Circuit.outputs circuit) ~f:(fun s -> uid s) :: ranks |> List.rev
  in
  let nranks = List.length ranks in
  (* write the bit to the left *)
  fprintf chan "digraph %s {\n" (Circuit.name circuit);
  for i = 0 to nranks - 1 do
    fprintf chan "%i" i;
    if i <> nranks - 1 then fprintf chan " -> "
  done;
  fprintf chan "\n";
  List.iteri ranks ~f:(fun i s ->
    fprintf chan " { rank=same; %i [shape=plaintext];\n" i;
    List.iter s ~f:(fun s -> fprintf chan "  _%Li;\n" s);
    fprintf chan "}\n");
  (* write edges *)
  Signal_graph.iter (Circuit.signal_graph circuit) ~f:(fun s ->
    List.iter (fdeps s) ~f:(fun d -> fprintf chan "_%Li -> _%Li;\n" (uid d) (uid s)));
  fprintf chan "}\n"
;;

(* GDL file with manhatten layout - looks much, much nicer *)
let write_gdl
      ?(names = false)
      ?(widths = false)
      ?(consts = true)
      ?(clocks = false)
      chan
      circuit
  =
  let quote s = "\"" ^ s ^ "\"" in
  fprintf chan "graph: {\n";
  let props =
    [ "title", quote (Circuit.name circuit)
    ; "manhattenedges", "yes"
    ; "inportsharing", "no"
    ; "outportsharing", "yes"
    ; "node.bordercolor", "lightblue"
    ]
  in
  let props = if widths then ("display_edge_labels", "yes") :: props else props in
  (* write list of default attributes *)
  List.iter props ~f:(fun (a, b) -> fprintf chan "%s: %s\n" a b);
  let folds c s =
    List.fold s ~init:"" ~f:(fun s n -> if String.is_empty s then n else n ^ c ^ s)
  in
  let name s =
    let names = Signal.names s in
    match names with
    | [] -> ""
    | [ h ] -> h
    | h :: t -> h ^ " (" ^ folds "," t ^ ")"
  in
  let write_node
        ?(border = "invisible")
        ?(shape = "box")
        ?(label = "")
        ?(bordercolour = "")
        ?(colour = "")
        ?(textcolour = "")
        signal
    =
    fprintf chan "node: { title: \"%Li\" " (uid signal);
    let name = if String.is_empty label || names then name signal else "" in
    (match label, name with
     | "", "" -> fprintf chan "label: \"none\" "
     | _, "" -> fprintf chan "label: \"%s\" " label
     | "", _ -> fprintf chan "label: \"\\fI%s\" " name
     | _ -> fprintf chan "label: \"%s\\n\\fI%s\" " label name);
    fprintf chan "shape: %s " shape;
    fprintf chan "borderstyle: %s " border;
    if not (String.is_empty textcolour) then fprintf chan "textcolor: %s " textcolour;
    if not (String.is_empty bordercolour)
    then fprintf chan "bordercolor: %s " bordercolour;
    if not (String.is_empty colour) then fprintf chan "color: %s " colour;
    fprintf chan " }\n"
  in
  let is_rom s =
    match s with
    | Mux _ ->
      List.fold (List.tl_exn (deps s)) ~init:true ~f:(fun b s -> b && is_const s)
    | _ -> false
  in
  let reg_deps s =
    match s with
    | Reg { register = r; d; _ } ->
      (if clocks then [ r.reg_clock ] else []) @ [ d; r.reg_enable ]
    | _ -> []
  in
  let mem_deps s =
    match s with
    | Mem { register = r; memory = m; _ } ->
      [ m.mem_write_data; r.reg_enable; m.mem_read_address; m.mem_write_address ]
    | Multiport_mem { write_ports; _ } ->
      Array.map write_ports ~f:(fun wr ->
        [ wr.write_clock; wr.write_enable; wr.write_address; wr.write_data ])
      |> Array.to_list
      |> List.concat
    | _ -> []
  in
  let is_input s = Circuit.is_input circuit s in
  let is_output s = Circuit.is_output circuit s in
  (* write nodes *)
  let write_node s =
    match s with
    | Empty -> write_node ~label:"empty" s
    | Const { constant; _ } ->
      write_node
        ~label:(Bits.to_constant constant |> Constant.to_hex_string ~signedness:Unsigned)
        s
    | Wire _ ->
      if List.is_empty (Signal.names s)
      then write_node ~textcolour:"lightgrey" ~label:"wire" s
      else if is_input s
      then write_node ~textcolour:"red" s
      else if is_output s
      then write_node ~textcolour:"red" s
      else write_node ~textcolour:"lightgrey" s
    | Select { high; low; _ } ->
      write_node ~textcolour:"lightgrey" ~label:(sprintf "[%i:%i]" high low) s
    | Op2 { op; _ } ->
      (match op with
       | Signal_add -> write_node ~border:"solid" ~shape:"circle" ~label:"+" s
       | Signal_sub -> write_node ~border:"solid" ~shape:"circle" ~label:"-" s
       | Signal_mulu -> write_node ~border:"solid" ~shape:"circle" ~label:"*" s
       | Signal_muls -> write_node ~border:"solid" ~shape:"circle" ~label:"*+" s
       | Signal_and -> write_node ~border:"solid" ~shape:"circle" ~label:"&" s
       | Signal_or -> write_node ~border:"solid" ~shape:"circle" ~label:"|" s
       | Signal_xor -> write_node ~border:"solid" ~shape:"circle" ~label:"^" s
       | Signal_eq -> write_node ~border:"solid" ~shape:"circle" ~label:"=" s
       | Signal_lt -> write_node ~border:"solid" ~shape:"circle" ~label:"<" s)
    | Not _ -> write_node ~border:"solid" ~shape:"circle" ~label:"~" s
    | Mux _ ->
      if is_rom s
      then (
        let els = List.length (deps s) - 1 in
        write_node ~border:"solid" ~shape:"box" ~label:(sprintf "rom%i" els) s)
      else write_node ~border:"solid" ~shape:"uptrapeze" ~label:"mux" s
    | Cat _ -> write_node ~border:"solid" ~shape:"trapeze" ~label:"cat" s
    | Reg _ ->
      write_node
        ~bordercolour:"lightblue"
        ~textcolour:"white"
        ~colour:"black"
        ~border:"solid"
        ~label:"reg"
        s
    | Mem { memory = m; _ } ->
      write_node
        ~bordercolour:"lightblue"
        ~textcolour:"white"
        ~colour:"black"
        ~border:"solid"
        ~label:(sprintf "mem%i" m.mem_size)
        s
    | Multiport_mem { size; _ } ->
      write_node
        ~bordercolour:"lightblue"
        ~textcolour:"white"
        ~colour:"black"
        ~border:"solid"
        ~label:(sprintf "mem%i" size)
        s
    | Mem_read_port _ ->
      write_node
        ~bordercolour:"lightblue"
        ~textcolour:"white"
        ~colour:"black"
        ~border:"solid"
        ~label:"mem_rdp"
        s
    | Inst { instantiation; _ } ->
      write_node ~border:"solid" ~label:(sprintf "inst\n%s" instantiation.inst_name) s
  in
  (* specialised dependancies *)
  let deps s =
    if is_rom s
    then [ List.hd_exn (deps s) ]
    else if is_reg s
    then reg_deps s
    else if is_mem s
    then mem_deps s
    else deps s
  in
  (* write edges *)
  let write_edges () =
    Signal_graph.depth_first_search
      (Circuit.signal_graph circuit)
      ~init:(Set.empty (module Uid))
      ~f_before:(fun a s ->
        let deps = deps s |> List.filter ~f:(fun t -> not (is_empty t)) in
        let deps =
          if consts then deps else deps |> List.filter ~f:(fun s -> not (is_const s))
        in
        if (not (List.is_empty deps)) && not (is_empty s)
        then (
          List.iter deps ~f:(fun d ->
            (* Note; labels always specified, even if they are disabled *)
            fprintf chan "edge: { source: \"%Li\" target: \"%Li\" " (uid d) (uid s);
            if (is_wire s && not (is_output s)) || is_select s
            then fprintf chan "arrowstyle: none ";
            fprintf chan "color:lightgrey thickness: 1 label: \"%i\" }\n" (width d));
          List.fold (s :: deps) ~init:a ~f:(fun a s -> Set.add a (uid s)))
        else a)
  in
  let nodes = write_edges () in
  Set.iter nodes ~f:(fun u -> write_node (Circuit.find_signal_exn circuit u));
  fprintf chan "}\n"
;;

let aisee3
      ?(args = "")
      ?(names = false)
      ?(widths = false)
      ?(consts = true)
      ?(clocks = false)
      circuit
  =
  let name, file = Filename.open_temp_file "aisee3" ".gdl" in
  write_gdl ~names ~widths ~consts ~clocks file circuit;
  Out_channel.close file;
  ignore (Unix.open_process_in ("aisee3 " ^ name ^ " " ^ args) : Stdio.In_channel.t)
;;
OCaml

Innovation. Community. Security.