Skip to content

Commit

Permalink
can2040: Optimize unstuf_pull_bits() on rp2350
Browse files Browse the repository at this point in the history
It is possible to use the ARM clz instruction to optimize bit
unstuffing.  (This instruction is not present on cortex-m0+ chips so
can't be used on the rp2040.)

Signed-off-by: Kevin O'Connor <[email protected]>
  • Loading branch information
KevinOConnor committed Dec 13, 2024
1 parent 76de773 commit 9516202
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 2 deletions.
46 changes: 45 additions & 1 deletion scripts/bitstuf.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env python
# Tool for testing bitstuffing implementations
#
# Copyright (C) 2022 Kevin O'Connor <[email protected]>
# Copyright (C) 2022-2024 Kevin O'Connor <[email protected]>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import sys
Expand All @@ -11,6 +11,10 @@
def report(msg):
sys.stderr.write("\n" + msg + "\n")

def clz(v):
v &= 0xffffffff
return 32 - v.bit_length()

LOOP = UNLOOP = 0

def bitstuf(b, num_bits):
Expand Down Expand Up @@ -174,6 +178,46 @@ def bitunstuf_batch_pass4(sb, num_bits):
else:
try_cnt //= 2

def bitunstuf_clz(sb, num_bits):
global UNLOOP
edges = sb ^ (sb >> 1)
e2 = edges | (edges >> 1)
e4 = e2 | (e2 >> 2)
rm_bits = ~e4
unstuffed_bits = 0
cu = TESTBITS
cs = num_bits
while 1:
UNLOOP += 1
if not cs:
# Need more data
return -999
try_cnt = cu if cu < cs else cs
try_mask = ((1 << try_cnt) - 1) << (cs + 1 - try_cnt)
rm_masked_bits = rm_bits & try_mask
if not rm_masked_bits:
# No stuff bits in try_cnt bits - copy into unstuffed_bits
cu -= try_cnt
cs -= try_cnt
unstuffed_bits |= ((sb >> cs) & ((1 << try_cnt) - 1)) << cu
if not cu:
# Extracted desired bits
return unstuffed_bits
# Need more data
return -999
# Copy any leading bits prior to stuff bit (may be zero)
copy_cnt = cs - (31 - clz(rm_masked_bits))
cs -= copy_cnt
cu -= copy_cnt
unstuffed_bits |= ((sb >> cs) & ((1 << copy_cnt) - 1)) << cu
# High bit is now a stuff bit - remove it
cs -= 1
if rm_bits & (1 << cs):
# Six consecutive bits - a bitstuff error
if (sb >> cs) & 1:
return -cs
return -cs

def main():
stuf_func = bitstuf_batch
unstuf_func = bitunstuf_batch
Expand Down
45 changes: 44 additions & 1 deletion src/can2040.c
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ unstuf_restore_state(struct can2040_bitunstuffer *bu, uint32_t data)

// Pull bits from unstuffer (as specified in unstuf_set_count() )
static int
unstuf_pull_bits(struct can2040_bitunstuffer *bu)
unstuf_pull_bits_rp2040(struct can2040_bitunstuffer *bu)
{
uint32_t sb = bu->stuffed_bits, edges = sb ^ (sb >> 1);
uint32_t e2 = edges | (edges >> 1), e4 = e2 | (e2 >> 2), rm_bits = ~e4;
Expand Down Expand Up @@ -550,6 +550,49 @@ unstuf_pull_bits(struct can2040_bitunstuffer *bu)
}
}

// Pull bits from unstuffer (optimized for rp2350)
static int
unstuf_pull_bits(struct can2040_bitunstuffer *bu)
{
if (!IS_RP2350)
return unstuf_pull_bits_rp2040(bu);
uint32_t sb = bu->stuffed_bits, edges = sb ^ (sb >> 1);
uint32_t e2 = edges | (edges >> 1), e4 = e2 | (e2 >> 2), rm_bits = ~e4;
uint32_t cs = bu->count_stuff, cu = bu->count_unstuff;
for (;;) {
if (!cs)
// Need more data
return 1;
uint32_t try_cnt = cs > cu ? cu : cs;
uint32_t try_mask = ((1 << try_cnt) - 1) << (cs + 1 - try_cnt);
uint32_t rm_masked_bits = rm_bits & try_mask;
if (likely(!rm_masked_bits)) {
// No stuff bits in try_cnt bits - copy into unstuffed_bits
bu->count_unstuff = cu = cu - try_cnt;
bu->count_stuff = cs = cs - try_cnt;
bu->unstuffed_bits |= ((sb >> cs) & ((1 << try_cnt) - 1)) << cu;
if (! cu)
// Extracted desired bits
return 0;
// Need more data
return 1;
}
// Copy any leading bits prior to stuff bit (may be zero)
uint32_t copy_cnt = cs - (31 - __builtin_clz(rm_masked_bits));
cs -= copy_cnt;
bu->count_unstuff = cu = cu - copy_cnt;
bu->unstuffed_bits |= ((sb >> cs) & ((1 << copy_cnt) - 1)) << cu;
// High bit is now a stuff bit - remove it
bu->count_stuff = cs = cs - 1;
if (unlikely(rm_bits & (1 << cs))) {
// Six consecutive bits - a bitstuff error
if (sb & (1 << cs))
return -1;
return -2;
}
}
}

// Return most recent raw (still stuffed) bits
static uint32_t
unstuf_get_raw(struct can2040_bitunstuffer *bu)
Expand Down

0 comments on commit 9516202

Please sign in to comment.