package hardcaml_of_verilog

  1. Overview
  2. Docs

Source file netlist.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
open! Base
module Bit = Yosys_netlist.Bit

module Bus = struct
  module T = struct
    type t = Bit.t list [@@deriving sexp_of, compare]
  end

  include T
  include Comparator.Make (T)
end

module Parameter = struct
  type t = Hardcaml.Parameter.t [@@deriving sexp_of]

  let of_yosys_netlist (p : Yosys_netlist.Parameter.t) =
    match p.value with
    | Int i -> Hardcaml.Parameter.create ~name:p.name ~value:(Int i)
    | String s -> Hardcaml.Parameter.create ~name:p.name ~value:(String s)
  ;;
end

module Port = struct
  type 'a t =
    { name : string
    ; value : 'a
    }
  [@@deriving sexp_of, fields ~getters]

  let find (ports : _ t list) name =
    let rec f = function
      | [] ->
        Or_error.error_s
          [%message "Could not find port" (name : string) (ports : _ t list)]
      | h :: t -> if String.equal h.name name then Ok h else f t
    in
    f ports
  ;;

  let find_exn ports name = find ports name |> Or_error.ok_exn
end

module Cell = struct
  type t =
    { module_name : string
    ; instance_name : string
    ; parameters : Parameter.t list
    ; inputs : Bus.t Port.t list
    ; outputs : Bus.t Port.t list
    }
  [@@deriving sexp_of]

  let partition_by_port_direction
    (cell : Yosys_netlist.Cell.t)
    (directions : Yosys_netlist.Direction.t Map.M(String).t)
    =
    let rec f inputs outputs (connections : Yosys_netlist.Connection.t list) =
      match connections with
      | [] -> Ok (inputs, outputs)
      | conn :: conns ->
        (match Map.find directions conn.name with
         | None ->
           Or_error.error_s
             [%message
               "No port direction specified"
                 (conn : Yosys_netlist.Connection.t)
                 (cell : Yosys_netlist.Cell.t)]
         | Some Input ->
           f ({ Port.name = conn.name; value = conn.value } :: inputs) outputs conns
         | Some Output ->
           f inputs ({ Port.name = conn.name; value = conn.value } :: outputs) conns)
    in
    f [] [] cell.value.connections
  ;;

  let of_yosys_netlist (cell : Yosys_netlist.Cell.t) =
    let%bind.Or_error directions =
      List.fold
        cell.value.port_directions
        ~init:(Ok (Map.empty (module String)))
        ~f:(fun map dirn ->
          let%bind.Or_error map = map in
          match Map.add map ~key:dirn.name ~data:dirn.value with
          | `Ok map -> Ok map
          | `Duplicate ->
            Or_error.error_s
              [%message
                "Port direction specified more than once"
                  (dirn : Yosys_netlist.Port_direction.t)
                  (cell : Yosys_netlist.Cell.t)])
    in
    let%bind.Or_error inputs, outputs = partition_by_port_direction cell directions in
    Ok
      { module_name = cell.value.module_name
      ; instance_name = cell.name
      ; parameters = List.map cell.value.parameters ~f:Parameter.of_yosys_netlist
      ; inputs
      ; outputs
      }
  ;;
end

(* Map of bus names from net indexes. *)
module Bus_names = struct
  type t = string list Map.M(Bus).t [@@deriving sexp_of]

  let add_to map ~key ~data =
    match Map.find map key with
    | None ->
      (* this wont raise, as we just check the key doesn't exist. *)
      Map.add_exn map ~key ~data:[ data ]
    | Some lst -> (* replace the key *) Map.set map ~key ~data:(data :: lst)
  ;;

  let create (netnames : Yosys_netlist.Netname.t list) : t =
    List.fold
      netnames
      ~init:(Map.empty (module Bus))
      ~f:(fun map netname ->
        if netname.value.hide_name = 1
        then map
        else add_to map ~key:netname.value.bits ~data:netname.name)
  ;;

  let find t bus =
    match Map.find t bus with
    | None -> []
    | Some l -> l
  ;;
end

module Module = struct
  type t =
    { name : string
    ; inputs : Bus.t Port.t list
    ; outputs : Bus.t Port.t list
    ; cells : Cell.t list
    ; bus_names : Bus_names.t
    }
  [@@deriving sexp_of]

  (* Partition ports of module into inputs and outputs *)
  let partition_module_ports (module_ : Yosys_netlist.Module.t) =
    let inputs, outputs =
      List.partition_tf module_.value.ports ~f:(fun p ->
        Yosys_netlist.Direction.(equal Input p.value.direction))
    in
    let to_ports ports =
      List.map
        (ports : Yosys_netlist.Port.t list)
        ~f:(fun p -> { Port.name = p.name; value = p.value.bits })
    in
    to_ports inputs, to_ports outputs
  ;;

  let of_yosys_netlist name (t : Yosys_netlist.Module.t) =
    let inputs, outputs = partition_module_ports t in
    let%bind.Or_error cells =
      List.map t.value.cells ~f:Cell.of_yosys_netlist |> Or_error.all
    in
    Ok { name; inputs; outputs; cells; bus_names = Bus_names.create t.value.netnames }
  ;;

  let sanitize_instance_names module_ =
    { module_ with
      cells =
        List.mapi module_.cells ~f:(fun index cell ->
          { cell with instance_name = "the_inst_" ^ Int.to_string index })
    }
  ;;
end

type t = (string * Module.t Or_error.t Lazy.t) list

let sexp_of_t t = [%sexp_of: string list] (List.map t ~f:fst)

let of_yosys_netlist (t : Yosys_netlist.t) =
  Or_error.try_with (fun () ->
    List.map t.modules ~f:(fun module_ ->
      module_.name, lazy (Module.of_yosys_netlist module_.name module_)))
;;

let find_module_by_name t name =
  match List.Assoc.find t ~equal:String.equal name with
  | None -> Or_error.error_s [%message "Failed to find requested module" (name : string)]
  | Some module_ -> Lazy.force module_
;;

let get_all_modules t =
  List.map t ~f:(fun (_, module_) -> Lazy.force module_) |> Or_error.all
;;

let create ?verbose ?passes verilog_design =
  let%bind.Or_error netlist =
    Synthesize.to_yosys_netlist ?verbose ?passes verilog_design
  in
  of_yosys_netlist netlist
;;