【ARM 嵌入式 C 入门及渐进 1 -- 移位算法&对齐算法】

Source

1.1 移位算法

1.1.1 乘法运算

a * 9 可以拆分成 a * (8+1)a*8+a*1, 因此可以改为:

 a=(a<<3)+a 

a = a * 7 分析 a*7 可以拆分成 a*(8-1)a*8-a*1, 因此可以改为:

a=(a<<3)-a 

1.1.2 取余运算

1)对 2 的 n 次方取余

a % b = a & (b-1) (b=2^n)

1.1.3 取模运算

1)对 2n 次方取模,N/8:

N>>3

2)对一个奇数取模,比如: num/9
使用 16(不能超过9的2倍) 替代 9 先对 num 取模,num 对 16 取模后的值肯定大于等于对 9 取模所得的值,使用移位算法可以进行乘 9 的计算,将所得的模值乘9之后再与原值 num 进行比较,第一次对 num进 行16 取模后,num - sum * 9的差值会分为三种情况:
1)小于 9,也即 num - 9*sum < 16 且小于9,所模值就为sum(只循环一次)
2) 大于等于9,小于16,也即,num-9*sum >= 9 且小于16,此时模值为 sum+1(只循环一次)
3)大于等于16,也即,num - 9*sum >= 16, 由于16>9, 所以模值 sum 太小,所以还要将 num-9*sum 的值再次对16 取模, 得到 sum1,将两次模值相加后,再进行 num -(sum+sum1)* 916 进行比较。将多次对16取模的值进行累加,直到num-(sum+sum1+...+sumx)*9 < 16即可。

int modulo9_func(int num)
{
    
      
    int i, sum = 0;
    int tmpsum = num;
    debug_info("input number is 0x%x\n", num);
    while (tmpsum >= 16) {
    
      
        i++;
        tmpsum = tmpsum >> 4;
        sum = sum + tmpsum;
        debug_info("%d time  >> 4 operation sum:%d\n", i, sum);
        tmpsum = num - (sum << 3) - sum;
    }
    if (tmpsum >= 9)
        return (sum + 1);
}
static int modulo7_func(int num)
{
    
      
    int i, sum = 0;
    int tmpsum = num;
    debug_info("input number is 0x%x\n", num);
    while (tmpsum >= 8) {
    
      
        i++;
        tmpsum = tmpsum >> 3;
        sum = sum + tmpsum;
        debug_info("num %d time  >> 3 operation, sum:%d\n", i, sum);
        tmpsum = num - (sum << 3) + sum;
    }
    if (tmpsum >= 7)
        return (sum + 1);
    return sum;
}

1.1.4 幂次方判断

static int power2_test(int num)
{
    
      
    return ((((num - 0x1) & num) == 0) && (num != 0));
}

1.2 对齐问题

字节对齐是在分配内存时需要考虑的问题,两个小算法:
假定目前该变量占用 n 个字节,对齐目标是 align , 求返回的占用实际内存大小。
(1) 最容易想到的算法:

unsigned int calc_align(unsigned int n,unsigned align)
{
    
      
    if ( n / align * align == n)
            return n;
 
    return  (n / align + 1) * align;
}

就是判断一下,当前占用的内存字节,是否是 alin 的整数倍。如果是那么直接返回,这个占用字节数;如果不满足,那么直接加一个 align 来对齐。只有当 n 是align 的整数倍的时候,可以实现等于号。

(2) 更好的算法:

#define ALIGN(n, align) ((n + align - 1) & ~(align - 1))

在RT-Thread 的代码中,关于 cache 的操作就用到了这个方法:

rt-thread/rt-thread/libcpu/arm/cortex-m7/cpu_cache.c

void rt_hw_cpu_dcache_ops(int ops, void* addr, int size)
{
    
      
    rt_uint32_t startAddr = (rt_uint32_t)addr & (rt_uint32_t)~(L1CACHE_LINESIZE_BYTE - 1);
    rt_uint32_t size_byte = size + (rt_uint32_t)addr - startAddr;
    uint32_t clean_invalid = RT_HW_CACHE_FLUSH | RT_HW_CACHE_INVALIDATE;
    if ((ops & clean_invalid) == clean_invalid)
    {
    
      
        SCB_CleanInvalidateDCache_by_Addr((uint32_t *)startAddr, size_byte);
    }
    else if (ops & RT_HW_CACHE_FLUSH)
    {
    
      
        SCB_CleanDCache_by_Addr((uint32_t *)startAddr, size_byte);
    }
    else if (ops & RT_HW_CACHE_INVALIDATE)
    {
    
      
        SCB_InvalidateDCache_by_Addr((uint32_t *)startAddr, size_byte);
    }
    else
    {
    
      
        RT_ASSERT(0);
    }
}