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
open Base
open Hardcaml
let check_cache = true
type t =
{ mutable data : Bytes.t
; mutable length : int
; width : int
; rounded_width : int
; log2_rounded_width : int
; mutable cached_bits : Bits.t
; mutable cached_sub_word : int
; cached_multi_word : Bytes.t
; cached_temp_multi_word : Bytes.t
; mutable non_cache_hits : int
}
[@@deriving sexp_of, compare, fields, equal]
let get64 = Stdlib.Bytes.get_int64_ne
let set64 = Stdlib.Bytes.set_int64_ne
let total_length length_in_bytes rounded_width =
if rounded_width < 8
then length_in_bytes * (8 / rounded_width)
else length_in_bytes / (rounded_width / 8)
;;
let[@cold] raise_invalid_width actual_width expected_width =
raise_s
[%message
"Unexpected [Bits.t] width in waveform" (actual_width : int) (expected_width : int)]
;;
let create width =
let rounded_width =
if width < 64 then Int.ceil_pow2 width else Int.round_up ~to_multiple_of:64 width
in
let length_in_bytes = max 8 (rounded_width / 8) in
{ data = Bytes.make length_in_bytes '\000'
; length = 0
; width
; rounded_width
; log2_rounded_width = Int.ceil_log2 rounded_width
; cached_bits = Bits.zero width
; cached_sub_word = 0
; cached_multi_word = Bytes.make length_in_bytes '\000'
; cached_temp_multi_word = Bytes.make length_in_bytes '\000'
; non_cache_hits = 0
}
;;
let resize t =
let length_in_bytes = Bytes.length t.data in
let data = Bytes.make (length_in_bytes * 2) '\000' in
Bytes.blit ~src:t.data ~src_pos:0 ~dst:data ~dst_pos:0 ~len:length_in_bytes;
t.data <- data
;;
let total_length t = total_length (Bytes.length t.data) t.rounded_width
let set_multi_word t index bits =
let underlying_repr = Bits.Expert.unsafe_underlying_repr bits in
let bytes_per_word = Bits.number_of_data_bytes bits in
Bytes.blit
~src:underlying_repr
~src_pos:Bits.Expert.offset_for_data
~dst:t.data
~dst_pos:(bytes_per_word * index)
~len:bytes_per_word
;;
let masks = Array.init 6 ~f:(fun i -> Int64.((1L lsl Int.(1 lsl i)) - 1L))
let set_sub_word t index bits =
let mask = masks.(t.log2_rounded_width) in
let shift = 6 - t.log2_rounded_width in
let byte_offset = (index lsr shift) lsl 3 in
let part_offset = (index land ((1 lsl shift) - 1)) lsl t.log2_rounded_width in
let new_bits = Bits.unsafe_get_int64 bits 0 in
let cur_bits = get64 t.data byte_offset in
let bits =
Int64.(cur_bits land lnot (mask lsl part_offset) lor (new_bits lsl part_offset))
in
set64 t.data byte_offset bits
;;
let rec set t index bits =
if index < total_length t
then
if Bits.width bits <> t.width
then raise_invalid_width (Bits.width bits : int) (t.width : int)
else (
if t.rounded_width < 64
then set_sub_word t index bits
else set_multi_word t index bits;
t.length <- max t.length (index + 1))
else (
resize t;
set t index bits)
;;
let get_multi_word t index =
let bytes_per_word = t.rounded_width lsr 3 in
Bytes.blit
~src:t.data
~src_pos:(bytes_per_word * index)
~dst:t.cached_temp_multi_word
~dst_pos:0
~len:bytes_per_word;
if check_cache && Bytes.equal t.cached_temp_multi_word t.cached_multi_word
then t.cached_bits
else (
let bits = Bits.zero t.width in
let bits_underlying_repr = Bits.Expert.unsafe_underlying_repr bits in
Bytes.blit
~src:t.cached_temp_multi_word
~src_pos:0
~dst:bits_underlying_repr
~dst_pos:Bits.Expert.offset_for_data
~len:bytes_per_word;
Bytes.blit
~src:t.cached_temp_multi_word
~src_pos:0
~dst:t.cached_multi_word
~dst_pos:0
~len:bytes_per_word;
t.non_cache_hits <- t.non_cache_hits + 1;
t.cached_bits <- bits;
bits)
;;
let get_sub_word t index =
let mask = masks.(t.log2_rounded_width) in
let shift = 6 - t.log2_rounded_width in
let byte_offset = (index lsr shift) lsl 3 in
let part_offset = (index land ((1 lsl shift) - 1)) lsl t.log2_rounded_width in
let bits = get64 t.data byte_offset in
let bits = Int64.((bits lsr part_offset) land mask) |> Int64.to_int_trunc in
if check_cache && bits = t.cached_sub_word
then t.cached_bits
else (
t.non_cache_hits <- t.non_cache_hits + 1;
t.cached_sub_word <- bits;
t.cached_bits <- Bits.of_int ~width:t.width bits;
t.cached_bits)
;;
let[@cold] raise_index_out_of_bounds index length =
raise_s [%message "Index into waveform is out of bounds" (index : int) (length : int)]
;;
let get t index =
if index >= t.length
then raise_index_out_of_bounds index t.length
else if t.rounded_width < 64
then get_sub_word t index
else get_multi_word t index
;;
let init length ~width ~f =
let t = create width in
for index = 0 to length - 1 do
set t index (f index)
done;
t
;;