Source file register_bank_intf.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
open Base
open Hardcaml

(** A bank of read/write registers connected to a Master interface.

    The register bank connects a set of IO ports in a user design to an
    [Internal_bus] master (which is, generally, a CPU).  The master device
    writes values over the bus and these appear on the [write_values] list for
    presentation to the user design.  The [read_values] list is provided by the
    user design and is read by the master.  *)
module type S = sig
  module Master_to_slave : Internal_bus_ports.Master_to_slave
  module Slave_to_master : Internal_bus_ports.Slave_to_master

  type result =
    { write_values : Signal.t With_valid.t list
    ; read_enables : Signal.t list
    }
  [@@deriving sexp_of]

  type t = (Signal.t Slave_to_master.t, result) Slave_with_data.t

  type pipelined_read_depth =
    { external_cycles : int
    ; internal_mux_cycles : int
    }

  (** Creates a register bank.

      {[
        let { slave; write_values } =
          create ~reg_spec ~master ~write_modes ~read_values
      ]}

      [write_modes] specifies what behavior the corresponding [write_value]
      should have: hold the value written, or toggle back to some known value
      after 1 cycle.

      [write_values] can be connected to [read_values] to create a read/write
      register.

      Writes ignore [master.write_byte_en], therefore only aligned 32 bit
      transfers are fully supported.

      [clear_write_values] clears write registers whose mode is configured with
      mode [internal_clear = true].
  *)

  val create
    :  ?pipelined_read_depth:
         pipelined_read_depth
         (* Default is zero internal and external cycles of latency *)
    -> Reg_spec.t
    -> clear_write_values:Signal.t
    -> master:Signal.t Master_to_slave.t
    -> write_modes:Register_mode.t list
    -> read_values:Signal.t list
    -> t

  module With_interface (Read : Interface.S) (Write : Interface.S) : sig
    module Write_with_valid : Interface.S with type 'a t = 'a With_valid.t Write.t
    module Read_enable : Interface.S with type 'a t = 'a Read.t

    module I : sig
      type 'a t =
        { clock : 'a
        ; clear : 'a
        ; clear_write_values : 'a
        ; master : 'a Master_to_slave.t
        ; read_values : 'a Read.t
        }
      [@@deriving hardcaml]
    end

    module O : sig
      type 'a t =
        { slave : 'a Slave_to_master.t
        ; write_values : 'a Write_with_valid.t
        ; read_enable : 'a Read_enable.t
            (** [read_enable] is a single bit for each field in the register interface that
            toggles high for one cycle when that register is accessed. For multi-part
            registers, this allows the designer to implement any desired synchronisation
            between the individual fields. *)
        }
      [@@deriving hardcaml]
    end

    val write_addresses : int Write.t
    val read_addresses : int Read.t

    val create
      :  ?pipelined_read_depth:pipelined_read_depth
      -> Scope.t
      -> write_modes:Register_mode.t Write.t
      -> Interface.Create_fn(I)(O).t

    val hierarchical
      :  ?pipelined_read_depth:pipelined_read_depth
      -> Scope.t
      -> write_modes:Register_mode.t Write.t
      -> Interface.Create_fn(I)(O).t
  end
end

module type Register_bank = sig
  module type S = S

  (** Packed arrays are a flattened version of [X.t] represented as an array of 32 bit
      vectors.

      They may be used as fields within a register interface to encode larger or grouped
      values. *)
  module Packed_array (X : sig
    include Interface.S

    val name : string
  end) : sig
    include Interface.S with type 'a t = 'a array

    val to_packed_array : (module Comb.S with type t = 'a) -> 'a X.t -> 'a t

    val to_packed_array_latch_on_read
      :  read_latency:int
      -> Reg_spec.t
      -> Signal.t X.t
      -> Signal.t t
      -> Signal.t t

    val of_packed_array : (module Comb.S with type t = 'a) -> 'a t -> 'a X.t

    val of_packed_array_with_valid
      :  (module Comb.S with type t = 'a)
      -> 'a With_valid.t t
      -> 'a With_valid.t X.t

    (* Extract fields *)
    val extract_field_as_int : (int t -> int) X.t
    val extract_field_as_int64 : (int t -> int64) X.t
    val extract_field_as_bytes : (int t -> Bytes.t -> unit) X.t
    val extract_field_as_string : (int t -> String.t) X.t

    (* Set fields *)
    val set_field_as_int : (int t -> int -> unit) X.t
    val set_field_as_int64 : (int t -> int64 -> unit) X.t
    val set_field_as_bytes : (int t -> Bytes.t -> unit) X.t
    val set_field_as_string : (int t -> String.t -> unit) X.t
    val hold : Register_mode.t t

    (* Specialized conversions for ints *)
    val of_packed_int_array : int t -> int X.t
    val to_packed_int_array : int X.t -> int t
  end

  module Make
    (Master_to_slave : Internal_bus_ports.Master_to_slave)
    (Slave_to_master : Internal_bus_ports.Slave_to_master) :
    S
      with module Master_to_slave := Master_to_slave
       and module Slave_to_master := Slave_to_master
end