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/>.
20 #include "config.h" // For HAVE_UNALIGNED_LITTLE_ENDIAN_ACCESS
31 #include "logicsegment.hpp"
33 #include <libsigrokcxx/libsigrokcxx.hpp>
35 using std::lock_guard
;
36 using std::recursive_mutex
;
39 using std::shared_ptr
;
47 const int LogicSegment::MipMapScalePower
= 4;
48 const int LogicSegment::MipMapScaleFactor
= 1 << MipMapScalePower
;
49 const float LogicSegment::LogMipMapScaleFactor
= logf(MipMapScaleFactor
);
50 const uint64_t LogicSegment::MipMapDataUnit
= 64 * 1024; // bytes
52 LogicSegment::LogicSegment(pv::data::Logic
& owner
, uint32_t segment_id
,
53 unsigned int unit_size
, uint64_t samplerate
) :
54 Segment(segment_id
, samplerate
, unit_size
),
56 last_append_sample_(0),
57 last_append_accumulator_(0),
60 memset(mip_map_
, 0, sizeof(mip_map_
));
63 LogicSegment::~LogicSegment()
65 lock_guard
<recursive_mutex
> lock(mutex_
);
67 for (MipMapLevel
&l
: mip_map_
)
71 shared_ptr
<const LogicSegment
> LogicSegment::get_shared_ptr() const
73 shared_ptr
<const Segment
> ptr
= nullptr;
76 ptr
= shared_from_this();
77 } catch (std::exception
& e
) {
78 /* Do nothing, ptr remains a null pointer */
81 return ptr
? std::dynamic_pointer_cast
<const LogicSegment
>(ptr
) : nullptr;
85 void LogicSegment::downsampleTmain(const T
*&in
, T
&acc
, T
&prev
)
87 // Accumulate one sample at a time
88 for (uint64_t i
= 0; i
< MipMapScaleFactor
; i
++) {
96 void LogicSegment::downsampleTmain
<uint8_t>(const uint8_t*&in
, uint8_t &acc
, uint8_t &prev
)
98 // Handle 8 bit samples in 32 bit steps
99 uint32_t prev32
= prev
| prev
<< 8 | prev
<< 16 | prev
<< 24;
100 uint32_t acc32
= acc
;
101 const uint32_t *in32
= (const uint32_t*)in
;
102 for (uint64_t i
= 0; i
< MipMapScaleFactor
; i
+= 4) {
103 uint32_t sample32
= *in32
++;
104 acc32
|= prev32
^ sample32
;
107 // Reduce result back to uint8_t
108 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
109 prev
= (prev32
>> 24) & 0xff; // MSB is last
110 #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
111 prev
= prev32
& 0xff; // LSB is last
113 #error Endianness unknown
116 acc
|= (acc32
>> 8) & 0xff;
117 acc
|= (acc32
>> 16) & 0xff;
118 acc
|= (acc32
>> 24) & 0xff;
119 in
= (const uint8_t*)in32
;
123 void LogicSegment::downsampleTmain
<uint16_t>(const uint16_t*&in
, uint16_t &acc
, uint16_t &prev
)
125 // Handle 16 bit samples in 32 bit steps
126 uint32_t prev32
= prev
| prev
<< 16;
127 uint32_t acc32
= acc
;
128 const uint32_t *in32
= (const uint32_t*)in
;
129 for (uint64_t i
= 0; i
< MipMapScaleFactor
; i
+= 2) {
130 uint32_t sample32
= *in32
++;
131 acc32
|= prev32
^ sample32
;
134 // Reduce result back to uint16_t
135 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
136 prev
= (prev32
>> 16) & 0xffff; // MSB is last
137 #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
138 prev
= prev32
& 0xffff; // LSB is last
140 #error Endian unknown
142 acc
|= acc32
& 0xffff;
143 acc
|= (acc32
>> 16) & 0xffff;
144 in
= (const uint16_t*)in32
;
148 void LogicSegment::downsampleT(const uint8_t *in_
, uint8_t *&out_
, uint64_t len
)
150 const T
*in
= (const T
*)in_
;
152 T prev
= last_append_sample_
;
153 T acc
= last_append_accumulator_
;
155 // Try to complete the previous downsample
156 if (last_append_extra_
) {
157 while (last_append_extra_
< MipMapScaleFactor
&& len
> 0) {
159 acc
|= prev
^ sample
;
161 last_append_extra_
++;
165 // Not enough samples available to complete downsample
166 last_append_sample_
= prev
;
167 last_append_accumulator_
= acc
;
170 // We have a complete downsample
173 last_append_extra_
= 0;
176 // Handle complete blocks of MipMapScaleFactor samples
177 while (len
>= MipMapScaleFactor
) {
178 downsampleTmain
<T
>(in
, acc
, prev
);
179 len
-= MipMapScaleFactor
;
185 // Process remainder, not enough for a complete sample
188 acc
|= prev
^ sample
;
190 last_append_extra_
++;
195 last_append_sample_
= prev
;
196 last_append_accumulator_
= acc
;
197 out_
= (uint8_t *)out
;
200 void LogicSegment::downsampleGeneric(const uint8_t *in
, uint8_t *&out
, uint64_t len
)
202 // Downsample using the generic unpack_sample()
203 // which can handle any width between 1 and 8 bytes
204 uint64_t prev
= last_append_sample_
;
205 uint64_t acc
= last_append_accumulator_
;
207 // Try to complete the previous downsample
208 if (last_append_extra_
) {
209 while (last_append_extra_
< MipMapScaleFactor
&& len
> 0) {
210 const uint64_t sample
= unpack_sample(in
);
212 acc
|= prev
^ sample
;
214 last_append_extra_
++;
218 // Not enough samples available to complete downsample
219 last_append_sample_
= prev
;
220 last_append_accumulator_
= acc
;
223 // We have a complete downsample
224 pack_sample(out
, acc
);
227 last_append_extra_
= 0;
230 // Handle complete blocks of MipMapScaleFactor samples
231 while (len
>= MipMapScaleFactor
) {
232 // Accumulate one sample at a time
233 for (uint64_t i
= 0; i
< MipMapScaleFactor
; i
++) {
234 const uint64_t sample
= unpack_sample(in
);
236 acc
|= prev
^ sample
;
239 len
-= MipMapScaleFactor
;
241 pack_sample(out
, acc
);
246 // Process remainder, not enough for a complete sample
248 const uint64_t sample
= unpack_sample(in
);
250 acc
|= prev
^ sample
;
252 last_append_extra_
++;
257 last_append_sample_
= prev
;
258 last_append_accumulator_
= acc
;
261 inline uint64_t LogicSegment::unpack_sample(const uint8_t *ptr
) const
263 #ifdef HAVE_UNALIGNED_LITTLE_ENDIAN_ACCESS
264 return *(uint64_t*)ptr
;
267 switch (unit_size_
) {
269 value
|= ((uint64_t)ptr
[7]) << 56;
272 value
|= ((uint64_t)ptr
[6]) << 48;
275 value
|= ((uint64_t)ptr
[5]) << 40;
278 value
|= ((uint64_t)ptr
[4]) << 32;
281 value
|= ((uint32_t)ptr
[3]) << 24;
284 value
|= ((uint32_t)ptr
[2]) << 16;
287 value
|= ptr
[1] << 8;
299 inline void LogicSegment::pack_sample(uint8_t *ptr
, uint64_t value
)
301 #ifdef HAVE_UNALIGNED_LITTLE_ENDIAN_ACCESS
302 *(uint64_t*)ptr
= value
;
304 switch (unit_size_
) {
306 ptr
[7] = value
>> 56;
309 ptr
[6] = value
>> 48;
312 ptr
[5] = value
>> 40;
315 ptr
[4] = value
>> 32;
318 ptr
[3] = value
>> 24;
321 ptr
[2] = value
>> 16;
335 void LogicSegment::append_payload(shared_ptr
<sigrok::Logic
> logic
)
337 assert(unit_size_
== logic
->unit_size());
338 assert((logic
->data_length() % unit_size_
) == 0);
340 append_payload(logic
->data_pointer(), logic
->data_length());
343 void LogicSegment::append_payload(void *data
, uint64_t data_size
)
345 assert(unit_size_
> 0);
346 assert((data_size
% unit_size_
) == 0);
348 lock_guard
<recursive_mutex
> lock(mutex_
);
350 const uint64_t prev_sample_count
= sample_count_
;
351 const uint64_t sample_count
= data_size
/ unit_size_
;
353 append_samples(data
, sample_count
);
355 // Generate the first mip-map from the data
356 append_payload_to_mipmap();
358 if (sample_count
> 1)
359 owner_
.notify_samples_added(SharedPtrToSegment(shared_from_this()),
360 prev_sample_count
+ 1, prev_sample_count
+ 1 + sample_count
);
362 owner_
.notify_samples_added(SharedPtrToSegment(shared_from_this()),
363 prev_sample_count
+ 1, prev_sample_count
+ 1);
366 void LogicSegment::append_subsignal_payload(unsigned int index
, void *data
,
367 uint64_t data_size
, vector
<uint8_t>& destination
)
370 destination
.resize(data_size
* unit_size_
, 0);
372 // Set the bits for this sub-signal where needed
373 // Note: the bytes in *data must either be 0 or 1, nothing else
374 unsigned int index_byte_offs
= index
/ 8;
375 uint8_t* output_data
= destination
.data() + index_byte_offs
;
376 uint8_t* input_data
= (uint8_t*)data
;
378 for (uint64_t i
= 0; i
< data_size
; i
++) {
379 assert((i
* unit_size_
+ index_byte_offs
) < destination
.size());
380 *output_data
|= (input_data
[i
] << index
);
381 output_data
+= unit_size_
;
384 if (index
== owner_
.num_channels() - 1) {
385 // We gathered sample data of all sub-signals, let's append it
386 append_payload(destination
.data(), destination
.size());
391 void LogicSegment::get_samples(int64_t start_sample
,
392 int64_t end_sample
, uint8_t* dest
) const
394 assert(start_sample
>= 0);
395 assert(start_sample
<= (int64_t)sample_count_
);
396 assert(end_sample
>= 0);
397 assert(end_sample
<= (int64_t)sample_count_
);
398 assert(start_sample
<= end_sample
);
399 assert(dest
!= nullptr);
401 lock_guard
<recursive_mutex
> lock(mutex_
);
403 get_raw_samples(start_sample
, (end_sample
- start_sample
), dest
);
406 void LogicSegment::get_subsampled_edges(
407 vector
<EdgePair
> &edges
,
408 uint64_t start
, uint64_t end
,
409 float min_length
, int sig_index
, bool first_change_only
)
411 uint64_t index
= start
;
416 assert(start
<= end
);
417 assert(min_length
> 0);
418 assert(sig_index
>= 0);
419 assert(sig_index
< 64);
421 lock_guard
<recursive_mutex
> lock(mutex_
);
423 // Make sure we only process as many samples as we have
424 if (end
> get_sample_count())
425 end
= get_sample_count();
427 const uint64_t block_length
= (uint64_t)max(min_length
, 1.0f
);
428 const unsigned int min_level
= max((int)floorf(logf(min_length
) /
429 LogMipMapScaleFactor
) - 1, 0);
430 const uint64_t sig_mask
= 1ULL << sig_index
;
432 // Store the initial state
433 last_sample
= (get_unpacked_sample(start
) & sig_mask
) != 0;
434 if (!first_change_only
)
435 edges
.emplace_back(index
++, last_sample
);
437 while (index
+ block_length
<= end
) {
438 //----- Continue to search -----//
441 // We cannot fast-forward if there is no mip-map data at
442 // the minimum level.
443 fast_forward
= (mip_map_
[level
].data
!= nullptr);
445 if (min_length
< MipMapScaleFactor
) {
446 // Search individual samples up to the beginning of
447 // the next first level mip map block
448 const uint64_t final_index
= min(end
, pow2_ceil(index
, MipMapScalePower
));
450 for (; index
< final_index
&&
451 (index
& ~((uint64_t)(~0) << MipMapScalePower
)) != 0;
454 const bool sample
= (get_unpacked_sample(index
) & sig_mask
) != 0;
456 // If there was a change we cannot fast forward
457 if (sample
!= last_sample
) {
458 fast_forward
= false;
463 // If resolution is less than a mip map block,
464 // round up to the beginning of the mip-map block
465 // for this level of detail
466 const int min_level_scale_power
= (level
+ 1) * MipMapScalePower
;
467 index
= pow2_ceil(index
, min_level_scale_power
);
471 // We can fast forward only if there was no change
472 const bool sample
= (get_unpacked_sample(index
) & sig_mask
) != 0;
473 if (last_sample
!= sample
)
474 fast_forward
= false;
479 // Fast forward: This involves zooming out to higher
480 // levels of the mip map searching for changes, then
481 // zooming in on them to find the point where the edge
484 // Slide right and zoom out at the beginnings of mip-map
485 // blocks until we encounter a change
487 const int level_scale_power
= (level
+ 1) * MipMapScalePower
;
488 const uint64_t offset
= index
>> level_scale_power
;
490 // Check if we reached the last block at this
491 // level, or if there was a change in this block
492 if (offset
>= mip_map_
[level
].length
||
493 (get_subsample(level
, offset
) & sig_mask
))
496 if ((offset
& ~((uint64_t)(~0) << MipMapScalePower
)) == 0) {
497 // If we are now at the beginning of a
498 // higher level mip-map block ascend one
500 if ((level
+ 1 >= ScaleStepCount
) || (!mip_map_
[level
+ 1].data
))
505 // Slide right to the beginning of the
506 // next mip map block
507 index
= pow2_ceil(index
+ 1, level_scale_power
);
511 // Zoom in, and slide right until we encounter a change,
512 // and repeat until we reach min_level
514 assert(mip_map_
[level
].data
);
516 const int level_scale_power
= (level
+ 1) * MipMapScalePower
;
517 const uint64_t offset
= index
>> level_scale_power
;
519 // Check if we reached the last block at this
520 // level, or if there was a change in this block
521 if (offset
>= mip_map_
[level
].length
||
522 (get_subsample(level
, offset
) & sig_mask
)) {
523 // Zoom in unless we reached the minimum
525 if (level
== min_level
)
530 // Slide right to the beginning of the
531 // next mip map block
532 index
= pow2_ceil(index
+ 1, level_scale_power
);
536 // If individual samples within the limit of resolution,
537 // do a linear search for the next transition within the
539 if (min_length
< MipMapScaleFactor
) {
540 for (; index
< end
; index
++) {
541 const bool sample
= (get_unpacked_sample(index
) & sig_mask
) != 0;
542 if (sample
!= last_sample
)
548 //----- Store the edge -----//
550 // Take the last sample of the quanization block
551 const int64_t final_index
= index
+ block_length
;
552 if (index
+ block_length
> end
)
555 // Store the final state
556 const bool final_sample
= (get_unpacked_sample(final_index
- 1) & sig_mask
) != 0;
557 edges
.emplace_back(index
, final_sample
);
560 last_sample
= final_sample
;
562 if (first_change_only
)
566 // Add the final state
567 if (!first_change_only
) {
568 const bool end_sample
= get_unpacked_sample(end
) & sig_mask
;
569 if (last_sample
!= end_sample
)
570 edges
.emplace_back(end
, end_sample
);
571 edges
.emplace_back(end
+ 1, end_sample
);
575 void LogicSegment::get_surrounding_edges(vector
<EdgePair
> &dest
,
576 uint64_t origin_sample
, float min_length
, int sig_index
)
578 if (origin_sample
>= sample_count_
)
581 // Put the edges vector on the heap, it can become quite big until we can
582 // use a get_subsampled_edges() implementation that searches backwards
583 vector
<EdgePair
>* edges
= new vector
<EdgePair
>;
585 // Get all edges to the left of origin_sample
586 get_subsampled_edges(*edges
, 0, origin_sample
, min_length
, sig_index
, false);
588 // If we don't specify "first only", the first and last edge are the states
589 // at samples 0 and origin_sample. If only those exist, there are no edges
590 if (edges
->size() == 2) {
595 // Dismiss the entry for origin_sample so that back() gives us the
598 dest
.push_back(edges
->back());
601 // Get first edge to the right of origin_sample
602 get_subsampled_edges(*edges
, origin_sample
, sample_count_
, min_length
, sig_index
, true);
604 // "first only" is specified, so nothing needs to be dismissed
605 if (edges
->size() == 0) {
610 dest
.push_back(edges
->front());
615 void LogicSegment::reallocate_mipmap_level(MipMapLevel
&m
)
617 lock_guard
<recursive_mutex
> lock(mutex_
);
619 const uint64_t new_data_length
= ((m
.length
+ MipMapDataUnit
- 1) /
620 MipMapDataUnit
) * MipMapDataUnit
;
622 if (new_data_length
> m
.data_length
) {
623 m
.data_length
= new_data_length
;
625 // Padding is added to allow for the uint64_t write word
626 m
.data
= realloc(m
.data
, new_data_length
* unit_size_
+
631 void LogicSegment::append_payload_to_mipmap()
633 MipMapLevel
&m0
= mip_map_
[0];
634 uint64_t prev_length
;
636 SegmentDataIterator
* it
;
637 uint64_t accumulator
;
638 unsigned int diff_counter
;
640 // Expand the data buffer to fit the new samples
641 prev_length
= m0
.length
;
642 m0
.length
= sample_count_
/ MipMapScaleFactor
;
644 // Break off if there are no new samples to compute
645 if (m0
.length
== prev_length
)
648 reallocate_mipmap_level(m0
);
650 dest_ptr
= (uint8_t*)m0
.data
+ prev_length
* unit_size_
;
652 // Iterate through the samples to populate the first level mipmap
653 const uint64_t start_sample
= prev_length
* MipMapScaleFactor
;
654 const uint64_t end_sample
= m0
.length
* MipMapScaleFactor
;
655 uint64_t len_sample
= end_sample
- start_sample
;
656 it
= begin_sample_iteration(start_sample
);
657 while (len_sample
> 0) {
658 // Number of samples available in this chunk
659 uint64_t count
= get_iterator_valid_length(it
);
660 // Reduce if less than asked for
661 count
= std::min(count
, len_sample
);
662 uint8_t *src_ptr
= get_iterator_value(it
);
663 // Submit these contiguous samples to downsampling in bulk
665 downsampleT
<uint8_t>(src_ptr
, dest_ptr
, count
);
666 else if (unit_size_
== 2)
667 downsampleT
<uint16_t>(src_ptr
, dest_ptr
, count
);
668 else if (unit_size_
== 4)
669 downsampleT
<uint32_t>(src_ptr
, dest_ptr
, count
);
670 else if (unit_size_
== 8)
671 downsampleT
<uint64_t>(src_ptr
, dest_ptr
, count
);
673 downsampleGeneric(src_ptr
, dest_ptr
, count
);
675 // Advance iterator, should move to start of next chunk
676 continue_sample_iteration(it
, count
);
678 end_sample_iteration(it
);
680 // Compute higher level mipmaps
681 for (unsigned int level
= 1; level
< ScaleStepCount
; level
++) {
682 MipMapLevel
&m
= mip_map_
[level
];
683 const MipMapLevel
&ml
= mip_map_
[level
- 1];
685 // Expand the data buffer to fit the new samples
686 prev_length
= m
.length
;
687 m
.length
= ml
.length
/ MipMapScaleFactor
;
689 // Break off if there are no more samples to be computed
690 if (m
.length
== prev_length
)
693 reallocate_mipmap_level(m
);
695 // Subsample the lower level
696 const uint8_t* src_ptr
= (uint8_t*)ml
.data
+
697 unit_size_
* prev_length
* MipMapScaleFactor
;
698 const uint8_t *const end_dest_ptr
=
699 (uint8_t*)m
.data
+ unit_size_
* m
.length
;
701 for (dest_ptr
= (uint8_t*)m
.data
+
702 unit_size_
* prev_length
;
703 dest_ptr
< end_dest_ptr
;
704 dest_ptr
+= unit_size_
) {
706 diff_counter
= MipMapScaleFactor
;
707 while (diff_counter
-- > 0) {
708 accumulator
|= unpack_sample(src_ptr
);
709 src_ptr
+= unit_size_
;
712 pack_sample(dest_ptr
, accumulator
);
717 uint64_t LogicSegment::get_unpacked_sample(uint64_t index
) const
719 assert(index
< sample_count_
);
721 assert(unit_size_
<= 8); // 8 * 8 = 64 channels
724 get_raw_samples(index
, 1, data
);
726 return unpack_sample(data
);
729 uint64_t LogicSegment::get_subsample(int level
, uint64_t offset
) const
732 assert(mip_map_
[level
].data
);
733 return unpack_sample((uint8_t*)mip_map_
[level
].data
+
734 unit_size_
* offset
);
737 uint64_t LogicSegment::pow2_ceil(uint64_t x
, unsigned int power
)
739 const uint64_t p
= UINT64_C(1) << power
;
740 return (x
+ p
- 1) / p
* p
;