Source file circuit_to_json.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
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
open Hardcaml
open Base
open Yosys_netlist

let print_s = Stdio.Out_channel.print_s

let port_name signal =
  match Signal.names signal with
  | [ name ] -> name
  | _ -> raise_s [%message "Invalid circuit port name"]
;;

let signal_op_to_string op =
  match (op : Signal.signal_op) with
  | Signal_add -> "$add"
  | Signal_sub -> "$sub"
  | Signal_mulu -> "$mulu"
  | Signal_muls -> "$muls"
  | Signal_and -> "$and"
  | Signal_or -> "$or"
  | Signal_xor -> "$xor"
  | Signal_eq -> "$eq"
  | Signal_lt -> "$lt"
;;

let create_module ~debug circuit =
  (* Create a set of signals we aren't rendering, so we should ignore them. *)
  let ignore_set = ref (Set.empty (module Int64)) in
  Signal_graph.iter (Circuit.signal_graph circuit) ~f:(fun signal ->
    match signal with
    | Reg { register; _ } ->
      ignore_set := Set.add !ignore_set (Signal.uid register.reg_clear_value);
      ignore_set := Set.add !ignore_set (Signal.uid register.reg_reset_value)
    | _ -> ());
  (* Create a map of signal uids which will be outputs of instances, with a list of
     selects driven by that uid. This will be used to correctly assign signals to outputs
     of instances. *)
  let select_map = ref (Map.empty (module Int64)) in
  Signal_graph.iter (Circuit.signal_graph circuit) ~f:(fun signal ->
    match signal with
    | Inst { signal_id; _ } ->
      select_map := Map.set !select_map ~key:signal_id.s_id ~data:[]
    | _ -> ());
  Signal_graph.iter (Circuit.signal_graph circuit) ~f:(fun signal ->
    match signal with
    | Select { arg; signal_id; high; low } ->
      (* Only add it if it is driven by an Inst output. *)
      (match Map.find !select_map (Signal.uid arg) with
       | Some v ->
         select_map
         := Map.set
              !select_map
              ~key:(Signal.uid arg)
              ~data:((signal_id.s_id, high, low) :: v)
       | None -> ())
    | _ -> ());
  (* We create a map of signal_ids that when seen we want to replace the signal_id, this
     is used when dealing with wires. *)
  let driver_map = ref (Map.empty (module Int64)) in
  Signal_graph.iter (Circuit.signal_graph circuit) ~f:(fun signal ->
    match signal with
    | Wire { signal_id; driver } ->
      if Signal.is_empty !driver
      then ()
      else (
        match Map.add !driver_map ~key:signal_id.s_id ~data:(Signal.uid !driver) with
        | `Ok new_map -> driver_map := new_map
        | _ -> ())
    | _ -> ());
  if debug
  then (
    print_s [%message (!ignore_set : Set.M(Int64).t)];
    print_s [%message (!driver_map : int64 Map.M(Int64).t)];
    print_s [%message (!select_map : (int64 * int * int) list Map.M(Int64).t)]);
  let rec get_driver s_id =
    match Map.find !driver_map s_id with
    | Some v -> get_driver v
    | None -> s_id
  in
  let bit_name_of_uid uid = Bit.Index (uid |> get_driver |> Int64.to_int_exn) in
  let bit_name_of_signal signal =
    Bit.Index (Signal.uid signal |> get_driver |> Int64.to_int_exn)
  in
  let create_cells circuit =
    (* let default_attributes : attributes =
     *   { src = ""; full_case = 0; parallel_case = 0; init = None; unused_bits = None }
     * in *)
    let default_cell =
      { Cell.V.module_name = ""
      ; parameters = []
      ; port_directions = []
      ; connections = []
      ; hide_name = 0
      }
    in
    let cells = ref ([] : (string * Cell.V.t) list) in
    Signal_graph.iter (Circuit.signal_graph circuit) ~f:(fun signal ->
      if debug then Stdio.printf "%s\n" (Signal.to_string signal);
      let cell =
        let connections =
          List.map ~f:(fun (name, bits) -> Connection.{ name; value = bits })
        in
        let port_dirns =
          List.map ~f:(fun (name, dirn) -> Port_direction.{ name; value = dirn })
        in
        let open Direction in
        match signal with
        | Reg { d; signal_id; register } ->
          Some
            ( "$procdff$" ^ Int64.to_string signal_id.s_id
            , { default_cell with
                module_name = "$our_dff"
              ; connections =
                  [ "D", [ bit_name_of_signal d ]
                  ; "CLR", [ bit_name_of_signal register.reg_clear ]
                  ; "RST", [ bit_name_of_signal register.reg_reset ]
                  ; "CLK", [ bit_name_of_signal register.reg_clock ]
                  ; "CE", [ bit_name_of_signal register.reg_enable ]
                  ; "Q", [ bit_name_of_uid signal_id.s_id ]
                  ]
                  |> connections
              ; port_directions =
                  [ "CLK", Input
                  ; "CE", Input
                  ; "CLR", Input
                  ; "RST", Input
                  ; "D", Input
                  ; "Q", Output
                  ]
                  |> port_dirns
              } )
        | Cat { signal_id; args } ->
          Some
            ( "$mygate" ^ Int64.to_string signal_id.s_id
            , { default_cell with
                module_name = "$cat"
              ; connections =
                  [ "A", List.map args ~f:bit_name_of_signal
                  ; "Y", [ bit_name_of_uid signal_id.s_id ]
                  ]
                  |> connections
              ; port_directions = [ "A", Input; "Y", Output ] |> port_dirns
              } )
        | Empty -> None
        | Const { signal_id; constant } ->
          if Set.exists !ignore_set ~f:(Int64.( = ) signal_id.s_id)
          then None
          else (
            let name =
              "$"
              ^ (if Bits.is_vdd constant
                 then "vdd"
                 else if Bits.is_gnd constant
                 then "gnd"
                 else "const " ^ Int.Hex.to_string (Bits.to_int constant))
              ^ "_"
              ^ Int64.to_string signal_id.s_id
            in
            Some
              ( name
              , { default_cell with
                  module_name = name
                ; connections =
                    [ "Y", [ bit_name_of_uid signal_id.s_id ] ] |> connections
                ; port_directions = [ "Y", Output ] |> port_dirns
                } ))
        | Not { arg; signal_id } ->
          Some
            ( "$not" ^ Int64.to_string signal_id.s_id
            , { default_cell with
                module_name = "$inv"
              ; connections =
                  [ "A", [ bit_name_of_signal arg ]
                  ; "Y", [ bit_name_of_uid signal_id.s_id ]
                  ]
                  |> connections
              ; port_directions = [ "A", Input; "Y", Output ] |> port_dirns
              } )
        | Wire _ -> None
        | Select { arg; signal_id; high; low } ->
          (* Don't draw the select if it is driven by an Inst. *)
          (match Map.find !select_map (Signal.uid arg) with
           | None ->
             Some
               (let select_name =
                  "$select"
                  ^ Int64.to_string signal_id.s_id
                  ^ "["
                  ^ Int.to_string high
                  ^ ":"
                  ^ Int.to_string low
                  ^ "]"
                in
                ( select_name
                , { default_cell with
                    module_name = select_name
                  ; connections =
                      [ "A", [ bit_name_of_signal arg ]
                      ; "Y", [ bit_name_of_uid signal_id.s_id ]
                      ]
                      |> connections
                  ; port_directions = [ "A", Input; "Y", Output ] |> port_dirns
                  } ))
           | _ -> None)
        | Mem { signal_id; _ } ->
          Some
            ( "Memory"
            , { default_cell with
                module_name = "$mem"
              ; connections = [ "A", [ bit_name_of_uid signal_id.s_id ] ] |> connections
              ; port_directions = [ "A", Input ] |> port_dirns
              } )
        | Multiport_mem { signal_id; write_ports; _ } ->
          Some
            ( "$memory" ^ Int64.to_string signal_id.s_id
            , { default_cell with
                module_name = "$multiportmem"
              ; connections =
                  List.concat
                    Array.(
                      mapi write_ports ~f:(fun i a ->
                        [ ( "WR_DATA" ^ Int.to_string i
                          , [ bit_name_of_signal a.write_data ] )
                        ; ( "WR_EN" ^ Int.to_string i
                          , [ bit_name_of_signal a.write_enable ] )
                        ; ( "WR_ADDR" ^ Int.to_string i
                          , [ bit_name_of_signal a.write_address ] )
                        ; ( "WR_CLK" ^ Int.to_string i
                          , [ bit_name_of_signal a.write_clock ] )
                        ])
                      |> to_list)
                  @ [ "A", [ bit_name_of_uid signal_id.s_id ] ]
                  |> connections
              ; port_directions =
                  List.concat
                    Array.(
                      mapi write_ports ~f:(fun i _ ->
                        [ "WR_DATA" ^ Int.to_string i, Input
                        ; "WR_EN" ^ Int.to_string i, Input
                        ; "WR_ADDR" ^ Int.to_string i, Input
                        ; "WR_CLK" ^ Int.to_string i, Input
                        ])
                      |> to_list)
                  @ [ "A", Input ]
                  |> port_dirns
              } )
        | Mem_read_port { signal_id; _ } ->
          Some
            ( "$mem_read_port" ^ Int64.to_string signal_id.s_id
            , { default_cell with
                module_name = "$memreadport"
              ; connections = [ "A", [ bit_name_of_uid signal_id.s_id ] ] |> connections
              ; port_directions = [ "A", Input ] |> port_dirns
              } )
        | Op2 { signal_id; op; arg_a; arg_b } ->
          Some
            ( "$gate" ^ Int64.to_string signal_id.s_id
            , { default_cell with
                module_name = signal_op_to_string op
              ; connections =
                  [ "A", [ bit_name_of_signal arg_a ]
                  ; "B", [ bit_name_of_signal arg_b ]
                  ; "Y", [ bit_name_of_uid signal_id.s_id ]
                  ]
                  |> connections
              ; port_directions = [ "A", Input; "B", Input; "Y", Output ] |> port_dirns
              } )
        | Mux { signal_id; select; cases } ->
          Some
            ( "$mux" ^ Int64.to_string signal_id.s_id
            , { default_cell with
                module_name = "$our_mux"
              ; connections =
                  List.mapi cases ~f:(fun i a ->
                    "A" ^ Int.to_string i, [ bit_name_of_signal a ])
                  @ [ "S", [ bit_name_of_signal select ] ]
                  @ [ "Y", [ bit_name_of_uid signal_id.s_id ] ]
                  |> connections
              ; port_directions =
                  List.mapi cases ~f:(fun i _ -> "A" ^ Int.to_string i, Input)
                  @ [ "S", Input ]
                  @ [ "Y", Output ]
                  |> port_dirns
              } )
        | Inst { signal_id; instantiation; _ } ->
          (* Get the list of selects this instance drives. *)
          let selects = Map.find_exn !select_map signal_id.s_id in
          Some
            ( "$mygate" ^ Int64.to_string signal_id.s_id
            , { default_cell with
                module_name = "$inst_" ^ instantiation.inst_instance
              ; connections =
                  List.mapi instantiation.inst_inputs ~f:(fun _i (n, s) ->
                    n, [ bit_name_of_signal s ])
                  @ List.filter_map
                      instantiation.inst_outputs
                      ~f:(fun (n, (_width, o_lo)) ->
                        (* Try match each output with a select based on its hi and lo. *)
                        match
                          List.find selects ~f:(fun (_id, _hi, lo) -> o_lo = lo)
                        with
                        | Some (signal_id, _, _) ->
                          Some (n, [ bit_name_of_uid signal_id ])
                        | None -> None)
                  |> connections
              ; port_directions =
                  List.mapi instantiation.inst_inputs ~f:(fun _i (n, _s) -> n, Input)
                  @ List.filter_map
                      instantiation.inst_outputs
                      ~f:(fun (n, (_width, o_lo)) ->
                        match
                          List.find selects ~f:(fun (_id, _hi, lo) -> o_lo = lo)
                        with
                        | Some _ -> Some (n, Output)
                        | None -> None)
                  |> port_dirns
              } )
      in
      Option.iter cell ~f:(fun cell -> cells := cell :: !cells));
    !cells
  in
  let inputs =
    List.map (Circuit.inputs circuit) ~f:(fun input ->
      Port.
        { name = port_name input
        ; value = { direction = Input; bits = [ bit_name_of_signal input ] }
        })
  in
  let outputs =
    List.map (Circuit.outputs circuit) ~f:(fun output ->
      Port.
        { name = port_name output
        ; value = { direction = Output; bits = [ bit_name_of_signal output ] }
        })
  in
  { Module.name = Circuit.name circuit
  ; value =
      { ports = inputs @ outputs
      ; cells =
          List.map (create_cells circuit) ~f:(fun (name, cell) ->
            { Cell.name; value = cell })
      ; netnames = []
      }
  }
;;

let convert ?(debug = false) circuit =
  { creator = "hardcaml"; modules = [ create_module circuit ~debug ] }
;;