2 * This file is part of the PulseView project.
4 * Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
28 #include "logicsegment.hpp"
30 #include <libsigrokcxx/libsigrokcxx.hpp>
32 using std::lock_guard
;
33 using std::recursive_mutex
;
36 using std::shared_ptr
;
44 const int LogicSegment::MipMapScalePower
= 4;
45 const int LogicSegment::MipMapScaleFactor
= 1 << MipMapScalePower
;
46 const float LogicSegment::LogMipMapScaleFactor
= logf(MipMapScaleFactor
);
47 const uint64_t LogicSegment::MipMapDataUnit
= 64*1024; // bytes
49 LogicSegment::LogicSegment(pv::data::Logic
& owner
, unsigned int unit_size
,
50 uint64_t samplerate
) :
51 Segment(samplerate
, unit_size
),
53 last_append_sample_(0)
55 memset(mip_map_
, 0, sizeof(mip_map_
));
58 LogicSegment::~LogicSegment()
60 lock_guard
<recursive_mutex
> lock(mutex_
);
61 for (MipMapLevel
&l
: mip_map_
)
65 uint64_t LogicSegment::unpack_sample(const uint8_t *ptr
) const
67 #ifdef HAVE_UNALIGNED_LITTLE_ENDIAN_ACCESS
68 return *(uint64_t*)ptr
;
73 value
|= ((uint64_t)ptr
[7]) << 56;
76 value
|= ((uint64_t)ptr
[6]) << 48;
79 value
|= ((uint64_t)ptr
[5]) << 40;
82 value
|= ((uint64_t)ptr
[4]) << 32;
85 value
|= ((uint32_t)ptr
[3]) << 24;
88 value
|= ((uint32_t)ptr
[2]) << 16;
103 void LogicSegment::pack_sample(uint8_t *ptr
, uint64_t value
)
105 #ifdef HAVE_UNALIGNED_LITTLE_ENDIAN_ACCESS
106 *(uint64_t*)ptr
= value
;
108 switch (unit_size_
) {
110 ptr
[7] = value
>> 56;
113 ptr
[6] = value
>> 48;
116 ptr
[5] = value
>> 40;
119 ptr
[4] = value
>> 32;
122 ptr
[3] = value
>> 24;
125 ptr
[2] = value
>> 16;
139 void LogicSegment::append_payload(shared_ptr
<sigrok::Logic
> logic
)
141 assert(unit_size_
== logic
->unit_size());
142 assert((logic
->data_length() % unit_size_
) == 0);
144 append_payload(logic
->data_pointer(), logic
->data_length());
147 void LogicSegment::append_payload(void *data
, uint64_t data_size
)
149 assert((data_size
% unit_size_
) == 0);
151 lock_guard
<recursive_mutex
> lock(mutex_
);
153 uint64_t prev_sample_count
= sample_count_
;
154 uint64_t sample_count
= data_size
/ unit_size_
;
156 append_samples(data
, sample_count
);
158 // Generate the first mip-map from the data
159 append_payload_to_mipmap();
161 if (sample_count
> 1)
162 owner_
.notify_samples_added(this, prev_sample_count
+ 1,
163 prev_sample_count
+ 1 + sample_count
);
165 owner_
.notify_samples_added(this, prev_sample_count
+ 1,
166 prev_sample_count
+ 1);
169 const uint8_t* LogicSegment::get_samples(int64_t start_sample
,
170 int64_t end_sample
) const
172 assert(start_sample
>= 0);
173 assert(start_sample
<= (int64_t)sample_count_
);
174 assert(end_sample
>= 0);
175 assert(end_sample
<= (int64_t)sample_count_
);
176 assert(start_sample
<= end_sample
);
178 lock_guard
<recursive_mutex
> lock(mutex_
);
180 return get_raw_samples(start_sample
, (end_sample
-start_sample
));
183 SegmentLogicDataIterator
* LogicSegment::begin_sample_iteration(uint64_t start
)
185 return (SegmentLogicDataIterator
*)begin_raw_sample_iteration(start
);
188 void LogicSegment::continue_sample_iteration(SegmentLogicDataIterator
* it
, uint64_t increase
)
190 Segment::continue_raw_sample_iteration((SegmentRawDataIterator
*)it
, increase
);
193 void LogicSegment::end_sample_iteration(SegmentLogicDataIterator
* it
)
195 Segment::end_raw_sample_iteration((SegmentRawDataIterator
*)it
);
198 void LogicSegment::reallocate_mipmap_level(MipMapLevel
&m
)
200 lock_guard
<recursive_mutex
> lock(mutex_
);
202 const uint64_t new_data_length
= ((m
.length
+ MipMapDataUnit
- 1) /
203 MipMapDataUnit
) * MipMapDataUnit
;
205 if (new_data_length
> m
.data_length
) {
206 m
.data_length
= new_data_length
;
208 // Padding is added to allow for the uint64_t write word
209 m
.data
= realloc(m
.data
, new_data_length
* unit_size_
+
214 void LogicSegment::append_payload_to_mipmap()
216 MipMapLevel
&m0
= mip_map_
[0];
217 uint64_t prev_length
;
219 SegmentRawDataIterator
* it
;
220 uint64_t accumulator
;
221 unsigned int diff_counter
;
223 // Expand the data buffer to fit the new samples
224 prev_length
= m0
.length
;
225 m0
.length
= sample_count_
/ MipMapScaleFactor
;
227 // Break off if there are no new samples to compute
228 if (m0
.length
== prev_length
)
231 reallocate_mipmap_level(m0
);
233 dest_ptr
= (uint8_t*)m0
.data
+ prev_length
* unit_size_
;
235 // Iterate through the samples to populate the first level mipmap
236 uint64_t start_sample
= prev_length
* MipMapScaleFactor
;
237 uint64_t end_sample
= m0
.length
* MipMapScaleFactor
;
239 it
= begin_raw_sample_iteration(start_sample
);
240 for (uint64_t i
= start_sample
; i
< end_sample
;) {
241 // Accumulate transitions which have occurred in this sample
243 diff_counter
= MipMapScaleFactor
;
244 while (diff_counter
-- > 0) {
245 const uint64_t sample
= unpack_sample(it
->value
);
246 accumulator
|= last_append_sample_
^ sample
;
247 last_append_sample_
= sample
;
248 continue_raw_sample_iteration(it
, 1);
252 pack_sample(dest_ptr
, accumulator
);
253 dest_ptr
+= unit_size_
;
255 end_raw_sample_iteration(it
);
257 // Compute higher level mipmaps
258 for (unsigned int level
= 1; level
< ScaleStepCount
; level
++) {
259 MipMapLevel
&m
= mip_map_
[level
];
260 const MipMapLevel
&ml
= mip_map_
[level
-1];
262 // Expand the data buffer to fit the new samples
263 prev_length
= m
.length
;
264 m
.length
= ml
.length
/ MipMapScaleFactor
;
266 // Break off if there are no more samples to be computed
267 if (m
.length
== prev_length
)
270 reallocate_mipmap_level(m
);
272 // Subsample the lower level
273 const uint8_t* src_ptr
= (uint8_t*)ml
.data
+
274 unit_size_
* prev_length
* MipMapScaleFactor
;
275 const uint8_t *const end_dest_ptr
=
276 (uint8_t*)m
.data
+ unit_size_
* m
.length
;
278 for (dest_ptr
= (uint8_t*)m
.data
+
279 unit_size_
* prev_length
;
280 dest_ptr
< end_dest_ptr
;
281 dest_ptr
+= unit_size_
) {
283 diff_counter
= MipMapScaleFactor
;
284 while (diff_counter
-- > 0) {
285 accumulator
|= unpack_sample(src_ptr
);
286 src_ptr
+= unit_size_
;
289 pack_sample(dest_ptr
, accumulator
);
294 uint64_t LogicSegment::get_unpacked_sample(uint64_t index
) const
296 assert(index
< sample_count_
);
298 const uint8_t* data
= get_raw_samples(index
, 1);
299 uint64_t sample
= unpack_sample(data
);
305 void LogicSegment::get_subsampled_edges(
306 vector
<EdgePair
> &edges
,
307 uint64_t start
, uint64_t end
,
308 float min_length
, int sig_index
)
310 uint64_t index
= start
;
315 assert(end
<= get_sample_count());
316 assert(start
<= end
);
317 assert(min_length
> 0);
318 assert(sig_index
>= 0);
319 assert(sig_index
< 64);
321 lock_guard
<recursive_mutex
> lock(mutex_
);
323 const uint64_t block_length
= (uint64_t)max(min_length
, 1.0f
);
324 const unsigned int min_level
= max((int)floorf(logf(min_length
) /
325 LogMipMapScaleFactor
) - 1, 0);
326 const uint64_t sig_mask
= 1ULL << sig_index
;
328 // Store the initial state
329 last_sample
= (get_unpacked_sample(start
) & sig_mask
) != 0;
330 edges
.emplace_back(index
++, last_sample
);
332 while (index
+ block_length
<= end
) {
333 //----- Continue to search -----//
336 // We cannot fast-forward if there is no mip-map data at
337 // at the minimum level.
338 fast_forward
= (mip_map_
[level
].data
!= nullptr);
340 if (min_length
< MipMapScaleFactor
) {
341 // Search individual samples up to the beginning of
342 // the next first level mip map block
343 const uint64_t final_index
= min(end
,
344 pow2_ceil(index
, MipMapScalePower
));
346 for (; index
< final_index
&&
347 (index
& ~((uint64_t)(~0) << MipMapScalePower
)) != 0;
350 (get_unpacked_sample(index
) & sig_mask
) != 0;
352 // If there was a change we cannot fast forward
353 if (sample
!= last_sample
) {
354 fast_forward
= false;
359 // If resolution is less than a mip map block,
360 // round up to the beginning of the mip-map block
361 // for this level of detail
362 const int min_level_scale_power
=
363 (level
+ 1) * MipMapScalePower
;
364 index
= pow2_ceil(index
, min_level_scale_power
);
368 // We can fast forward only if there was no change
370 (get_unpacked_sample(index
) & sig_mask
) != 0;
371 if (last_sample
!= sample
)
372 fast_forward
= false;
377 // Fast forward: This involves zooming out to higher
378 // levels of the mip map searching for changes, then
379 // zooming in on them to find the point where the edge
382 // Slide right and zoom out at the beginnings of mip-map
383 // blocks until we encounter a change
385 const int level_scale_power
=
386 (level
+ 1) * MipMapScalePower
;
387 const uint64_t offset
=
388 index
>> level_scale_power
;
390 // Check if we reached the last block at this
391 // level, or if there was a change in this block
392 if (offset
>= mip_map_
[level
].length
||
393 (get_subsample(level
, offset
) &
397 if ((offset
& ~((uint64_t)(~0) << MipMapScalePower
)) == 0) {
398 // If we are now at the beginning of a
399 // higher level mip-map block ascend one
401 if (level
+ 1 >= ScaleStepCount
||
402 !mip_map_
[level
+ 1].data
)
407 // Slide right to the beginning of the
408 // next mip map block
409 index
= pow2_ceil(index
+ 1,
414 // Zoom in, and slide right until we encounter a change,
415 // and repeat until we reach min_level
417 assert(mip_map_
[level
].data
);
419 const int level_scale_power
=
420 (level
+ 1) * MipMapScalePower
;
421 const uint64_t offset
=
422 index
>> level_scale_power
;
424 // Check if we reached the last block at this
425 // level, or if there was a change in this block
426 if (offset
>= mip_map_
[level
].length
||
427 (get_subsample(level
, offset
) &
429 // Zoom in unless we reached the minimum
431 if (level
== min_level
)
436 // Slide right to the beginning of the
437 // next mip map block
438 index
= pow2_ceil(index
+ 1,
443 // If individual samples within the limit of resolution,
444 // do a linear search for the next transition within the
446 if (min_length
< MipMapScaleFactor
) {
447 for (; index
< end
; index
++) {
448 const bool sample
= (get_unpacked_sample(index
) &
450 if (sample
!= last_sample
)
456 //----- Store the edge -----//
458 // Take the last sample of the quanization block
459 const int64_t final_index
= index
+ block_length
;
460 if (index
+ block_length
> end
)
463 // Store the final state
464 const bool final_sample
=
465 (get_unpacked_sample(final_index
- 1) & sig_mask
) != 0;
466 edges
.emplace_back(index
, final_sample
);
469 last_sample
= final_sample
;
472 // Add the final state
473 const bool end_sample
= get_unpacked_sample(end
) & sig_mask
;
474 if (last_sample
!= end_sample
)
475 edges
.emplace_back(end
, end_sample
);
476 edges
.emplace_back(end
+ 1, end_sample
);
479 uint64_t LogicSegment::get_subsample(int level
, uint64_t offset
) const
482 assert(mip_map_
[level
].data
);
483 return unpack_sample((uint8_t*)mip_map_
[level
].data
+
484 unit_size_
* offset
);
487 uint64_t LogicSegment::pow2_ceil(uint64_t x
, unsigned int power
)
489 const uint64_t p
= 1 << power
;
490 return (x
+ p
- 1) / p
* p
;