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
;
37 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
, shared_ptr
<sigrok::Logic
> data
,
50 uint64_t samplerate
) :
51 Segment(samplerate
, data
->unit_size()),
53 last_append_sample_(0)
55 lock_guard
<recursive_mutex
> lock(mutex_
);
56 memset(mip_map_
, 0, sizeof(mip_map_
));
60 LogicSegment::~LogicSegment()
62 lock_guard
<recursive_mutex
> lock(mutex_
);
63 for (MipMapLevel
&l
: mip_map_
)
67 uint64_t LogicSegment::unpack_sample(const uint8_t *ptr
) const
69 #ifdef HAVE_UNALIGNED_LITTLE_ENDIAN_ACCESS
70 return *(uint64_t*)ptr
;
75 value
|= ((uint64_t)ptr
[7]) << 56;
78 value
|= ((uint64_t)ptr
[6]) << 48;
81 value
|= ((uint64_t)ptr
[5]) << 40;
84 value
|= ((uint64_t)ptr
[4]) << 32;
87 value
|= ((uint32_t)ptr
[3]) << 24;
90 value
|= ((uint32_t)ptr
[2]) << 16;
105 void LogicSegment::pack_sample(uint8_t *ptr
, uint64_t value
)
107 #ifdef HAVE_UNALIGNED_LITTLE_ENDIAN_ACCESS
108 *(uint64_t*)ptr
= value
;
110 switch (unit_size_
) {
112 ptr
[7] = value
>> 56;
115 ptr
[6] = value
>> 48;
118 ptr
[5] = value
>> 40;
121 ptr
[4] = value
>> 32;
124 ptr
[3] = value
>> 24;
127 ptr
[2] = value
>> 16;
141 void LogicSegment::append_payload(shared_ptr
<sigrok::Logic
> logic
)
143 assert(unit_size_
== logic
->unit_size());
144 assert((logic
->data_length() % unit_size_
) == 0);
146 lock_guard
<recursive_mutex
> lock(mutex_
);
148 uint64_t prev_sample_count
= sample_count_
;
149 uint64_t sample_count
= logic
->data_length() / unit_size_
;
151 append_samples(logic
->data_pointer(), sample_count
);
153 // Generate the first mip-map from the data
154 append_payload_to_mipmap();
156 if (sample_count
> 1)
157 owner_
.notify_samples_added(this, prev_sample_count
+ 1,
158 prev_sample_count
+ 1 + sample_count
);
160 owner_
.notify_samples_added(this, prev_sample_count
+ 1,
161 prev_sample_count
+ 1);
164 const uint8_t* LogicSegment::get_samples(int64_t start_sample
,
165 int64_t end_sample
) const
167 assert(start_sample
>= 0);
168 assert(start_sample
<= (int64_t)sample_count_
);
169 assert(end_sample
>= 0);
170 assert(end_sample
<= (int64_t)sample_count_
);
171 assert(start_sample
<= end_sample
);
173 lock_guard
<recursive_mutex
> lock(mutex_
);
175 return get_raw_samples(start_sample
, (end_sample
-start_sample
));
178 SegmentLogicDataIterator
* LogicSegment::begin_sample_iteration(uint64_t start
)
180 return (SegmentLogicDataIterator
*)begin_raw_sample_iteration(start
);
183 void LogicSegment::continue_sample_iteration(SegmentLogicDataIterator
* it
, uint64_t increase
)
185 Segment::continue_raw_sample_iteration((SegmentRawDataIterator
*)it
, increase
);
188 void LogicSegment::end_sample_iteration(SegmentLogicDataIterator
* it
)
190 Segment::end_raw_sample_iteration((SegmentRawDataIterator
*)it
);
193 void LogicSegment::reallocate_mipmap_level(MipMapLevel
&m
)
195 lock_guard
<recursive_mutex
> lock(mutex_
);
197 const uint64_t new_data_length
= ((m
.length
+ MipMapDataUnit
- 1) /
198 MipMapDataUnit
) * MipMapDataUnit
;
200 if (new_data_length
> m
.data_length
) {
201 m
.data_length
= new_data_length
;
203 // Padding is added to allow for the uint64_t write word
204 m
.data
= realloc(m
.data
, new_data_length
* unit_size_
+
209 void LogicSegment::append_payload_to_mipmap()
211 MipMapLevel
&m0
= mip_map_
[0];
212 uint64_t prev_length
;
214 SegmentRawDataIterator
* it
;
215 uint64_t accumulator
;
216 unsigned int diff_counter
;
218 // Expand the data buffer to fit the new samples
219 prev_length
= m0
.length
;
220 m0
.length
= sample_count_
/ MipMapScaleFactor
;
222 // Break off if there are no new samples to compute
223 if (m0
.length
== prev_length
)
226 reallocate_mipmap_level(m0
);
228 dest_ptr
= (uint8_t*)m0
.data
+ prev_length
* unit_size_
;
230 // Iterate through the samples to populate the first level mipmap
231 uint64_t start_sample
= prev_length
* MipMapScaleFactor
;
232 uint64_t end_sample
= m0
.length
* MipMapScaleFactor
;
234 it
= begin_raw_sample_iteration(start_sample
);
235 for (uint64_t i
= start_sample
; i
< end_sample
;) {
236 // Accumulate transitions which have occurred in this sample
238 diff_counter
= MipMapScaleFactor
;
239 while (diff_counter
-- > 0) {
240 const uint64_t sample
= unpack_sample(it
->value
);
241 accumulator
|= last_append_sample_
^ sample
;
242 last_append_sample_
= sample
;
243 continue_raw_sample_iteration(it
, 1);
247 pack_sample(dest_ptr
, accumulator
);
248 dest_ptr
+= unit_size_
;
250 end_raw_sample_iteration(it
);
252 // Compute higher level mipmaps
253 for (unsigned int level
= 1; level
< ScaleStepCount
; level
++) {
254 MipMapLevel
&m
= mip_map_
[level
];
255 const MipMapLevel
&ml
= mip_map_
[level
-1];
257 // Expand the data buffer to fit the new samples
258 prev_length
= m
.length
;
259 m
.length
= ml
.length
/ MipMapScaleFactor
;
261 // Break off if there are no more samples to be computed
262 if (m
.length
== prev_length
)
265 reallocate_mipmap_level(m
);
267 // Subsample the lower level
268 const uint8_t* src_ptr
= (uint8_t*)ml
.data
+
269 unit_size_
* prev_length
* MipMapScaleFactor
;
270 const uint8_t *const end_dest_ptr
=
271 (uint8_t*)m
.data
+ unit_size_
* m
.length
;
273 for (dest_ptr
= (uint8_t*)m
.data
+
274 unit_size_
* prev_length
;
275 dest_ptr
< end_dest_ptr
;
276 dest_ptr
+= unit_size_
) {
278 diff_counter
= MipMapScaleFactor
;
279 while (diff_counter
-- > 0) {
280 accumulator
|= unpack_sample(src_ptr
);
281 src_ptr
+= unit_size_
;
284 pack_sample(dest_ptr
, accumulator
);
289 uint64_t LogicSegment::get_unpacked_sample(uint64_t index
) const
291 assert(index
< sample_count_
);
293 const uint8_t* data
= get_raw_samples(index
, 1);
294 uint64_t sample
= unpack_sample(data
);
300 void LogicSegment::get_subsampled_edges(
301 std::vector
<EdgePair
> &edges
,
302 uint64_t start
, uint64_t end
,
303 float min_length
, int sig_index
)
305 uint64_t index
= start
;
310 assert(end
<= get_sample_count());
311 assert(start
<= end
);
312 assert(min_length
> 0);
313 assert(sig_index
>= 0);
314 assert(sig_index
< 64);
316 lock_guard
<recursive_mutex
> lock(mutex_
);
318 const uint64_t block_length
= (uint64_t)max(min_length
, 1.0f
);
319 const unsigned int min_level
= max((int)floorf(logf(min_length
) /
320 LogMipMapScaleFactor
) - 1, 0);
321 const uint64_t sig_mask
= 1ULL << sig_index
;
323 // Store the initial state
324 last_sample
= (get_unpacked_sample(start
) & sig_mask
) != 0;
325 edges
.push_back(pair
<int64_t, bool>(index
++, last_sample
));
327 while (index
+ block_length
<= end
) {
328 //----- Continue to search -----//
331 // We cannot fast-forward if there is no mip-map data at
332 // at the minimum level.
333 fast_forward
= (mip_map_
[level
].data
!= nullptr);
335 if (min_length
< MipMapScaleFactor
) {
336 // Search individual samples up to the beginning of
337 // the next first level mip map block
338 const uint64_t final_index
= min(end
,
339 pow2_ceil(index
, MipMapScalePower
));
341 for (; index
< final_index
&&
342 (index
& ~((uint64_t)(~0) << MipMapScalePower
)) != 0;
345 (get_unpacked_sample(index
) & sig_mask
) != 0;
347 // If there was a change we cannot fast forward
348 if (sample
!= last_sample
) {
349 fast_forward
= false;
354 // If resolution is less than a mip map block,
355 // round up to the beginning of the mip-map block
356 // for this level of detail
357 const int min_level_scale_power
=
358 (level
+ 1) * MipMapScalePower
;
359 index
= pow2_ceil(index
, min_level_scale_power
);
363 // We can fast forward only if there was no change
365 (get_unpacked_sample(index
) & sig_mask
) != 0;
366 if (last_sample
!= sample
)
367 fast_forward
= false;
372 // Fast forward: This involves zooming out to higher
373 // levels of the mip map searching for changes, then
374 // zooming in on them to find the point where the edge
377 // Slide right and zoom out at the beginnings of mip-map
378 // blocks until we encounter a change
380 const int level_scale_power
=
381 (level
+ 1) * MipMapScalePower
;
382 const uint64_t offset
=
383 index
>> level_scale_power
;
385 // Check if we reached the last block at this
386 // level, or if there was a change in this block
387 if (offset
>= mip_map_
[level
].length
||
388 (get_subsample(level
, offset
) &
392 if ((offset
& ~((uint64_t)(~0) << MipMapScalePower
)) == 0) {
393 // If we are now at the beginning of a
394 // higher level mip-map block ascend one
396 if (level
+ 1 >= ScaleStepCount
||
397 !mip_map_
[level
+ 1].data
)
402 // Slide right to the beginning of the
403 // next mip map block
404 index
= pow2_ceil(index
+ 1,
409 // Zoom in, and slide right until we encounter a change,
410 // and repeat until we reach min_level
412 assert(mip_map_
[level
].data
);
414 const int level_scale_power
=
415 (level
+ 1) * MipMapScalePower
;
416 const uint64_t offset
=
417 index
>> level_scale_power
;
419 // Check if we reached the last block at this
420 // level, or if there was a change in this block
421 if (offset
>= mip_map_
[level
].length
||
422 (get_subsample(level
, offset
) &
424 // Zoom in unless we reached the minimum
426 if (level
== min_level
)
431 // Slide right to the beginning of the
432 // next mip map block
433 index
= pow2_ceil(index
+ 1,
438 // If individual samples within the limit of resolution,
439 // do a linear search for the next transition within the
441 if (min_length
< MipMapScaleFactor
) {
442 for (; index
< end
; index
++) {
443 const bool sample
= (get_unpacked_sample(index
) &
445 if (sample
!= last_sample
)
451 //----- Store the edge -----//
453 // Take the last sample of the quanization block
454 const int64_t final_index
= index
+ block_length
;
455 if (index
+ block_length
> end
)
458 // Store the final state
459 const bool final_sample
=
460 (get_unpacked_sample(final_index
- 1) & sig_mask
) != 0;
461 edges
.push_back(pair
<int64_t, bool>(index
, final_sample
));
464 last_sample
= final_sample
;
467 // Add the final state
468 const bool end_sample
= get_unpacked_sample(end
) & sig_mask
;
469 if (last_sample
!= end_sample
)
470 edges
.push_back(pair
<int64_t, bool>(end
, end_sample
));
471 edges
.push_back(pair
<int64_t, bool>(end
+ 1, end_sample
));
474 uint64_t LogicSegment::get_subsample(int level
, uint64_t offset
) const
477 assert(mip_map_
[level
].data
);
478 return unpack_sample((uint8_t*)mip_map_
[level
].data
+
479 unit_size_
* offset
);
482 uint64_t LogicSegment::pow2_ceil(uint64_t x
, unsigned int power
)
484 const uint64_t p
= 1 << power
;
485 return (x
+ p
- 1) / p
* p
;