位带操作与应用手记

0x00 绪论

本片文章主要简述了SRAM方面的位带的使用说明,观看本文您需要有的前置经验有:

技能名称技能熟练度技能教程链接
单片机熟悉暂无
数据类型熟悉暂无
数字电路了解暂无
C语言熟悉暂无

主要从以下方面写:简述了位带的需求来源与详细设置。

0x01 位带概述

0x11 比特竞争

因为现在的芯片速度越来越快,对于数据的操作自然就更加的快,虽然我们的操作系统一般都会做的很好,但是抛开操作系统,直接对某些寄存器进行操作,可能就会出现很多意想不到的错误。这种操作主要是当我们需要操作一个RAM内存单位中的某一个bit时。

例如,定义一个按bit组成的定义位:

typedef union
{
    unsigned flag1:1;
    unsigned flag2:1;
    unsigned int all_flag;
}U_BIT_UNION;
U_BIT_UNION bit_union;

如果直接使用两个进程对这个数字进行竞争写入,则会出现比较尴尬的情况。

void Test1()
{
    ……
    bit_union.flag1 = 1;
    bit_union.flag2 = 0;
    sleep();		//等待一段时间,栓塞当前进程
	……
}
void Test2()
{
    ……
    bit_union.flag2 = 1;
    bit_union.flag1 = 0sleep();		//等待一段时间,栓塞当前进程
	……
}

在一般的编译器对于其的处理为:

Created with Raphaël 2.2.0 置一 读出数据 提取当前数据 写入当前数据 End
Created with Raphaël 2.2.0 置零 读出数据 提取当前数据 清除当前数据 End

当前的数据清除和数据写入本身没有什么问题,但是如果出现了竞争的情况,就会出现某个时期,两者都为1或者都为0的情况。因为每次操作,都会将数据取出,再写入,这种操作是非原子(atom)的。所以可能会出现:

Created with Raphaël 2.2.0 置一 置零 提取当前数据(置零) 清除当前数据(置零) 提取当前数据(置一) 写入当前数据(置一) End

这种情况,这种情况对于系统而言是十分不稳定的,因为可能在不可控的时间段内出现异常的清空和置位。

0x21比特栓塞

依托伟大的CS科学家们,他们发明了来处理这个情况。因为现代计算机的汇编语言约定俗成的机制为:自增和自减默认为原子操作(单个汇编指令),将锁的初始值设为1,当加锁的时候,仅需要自减即可清零。而读取则直接自加判断是否为一即可。这样就可以合理的栓塞住某些不切实际的修改,导致当前值出现异常的错误值。

One
Two
False
False
True
True
root
置1
置0
读取锁
读取锁
写入锁
操作

0x02 位带处理

这种处理方式广泛的存在于8086系列的操作系统中。而8086系列CPU因为历史原因无法实现从根源上解决的方案。(因为从8086之后的CPU我都没有过多的了解,如果以及解决了。请告诉我)。后起的ARM系列芯片使用了新的内核电路完成了对于其的原子写入。毕竟这种问题本质上也是一个硬件无法满足软件需求的问题。

0x12位带实现

实现的主要需求在于写入,而写入必须要完成原子操作,但是因为ARM系列为RISC指令集,不可能因为这个功能多设计两个汇编指令,于是IC工程师使用了其惯用的手法:映射.

映射是一种在IC设计中经常使用的方式,与当前软件行业中说的接口类似。而ARM的M系列芯片内,自带有一个映射区,这个映射区对应了整个RAM区的每一个单个BIT。并且将当前RAM的比特扩展为一个32位的布尔位(也就1bit相当于之前的数据全都是作废的,只有第一个bit支持写入)。这样就可以使用特殊的汇编组合,将写入RAM位于某个数据位的操作变为原子操作。

如上图,每一个数据在内部都是以寄存器(JK、D等硬件意义上的寄存器)的形式存在的,所以理论上来讲,映射是在现有条件下对于上层影响最小的方式。而我们只需要向指定的映射位置写入规定的数据即可。

0x22位带应用

位带使用则很简单,只需要将其在软件内定义为单个变量的写入即可。

#define BIT_SRAM(address,bit) __IO (SRAM_BASE+address*4)		//设定如此,举个例子,具体的设置需要参考具体的手册

设计时,也要保证当前索引的位置都是指定的位置,因为在64、32位MCU内,就会出现有可能的64个各个BIT。这样有可能在某些时间出现数错的情况……所以需要对当前设置项进行限制。

typedef union
{
    unsigned flag1:1;
    unsigned flag2:1;
    unsigned int all_flag;
}U_BIT_UNION;
U_BIT_STRUCT bit_union;
typedef enum
{
    FLAG_MIN = 0,
    FLAG_ONE,
    FLAG_TWE,
    FLAG_MAX
}E_BIT_UNION;

这样就可以将数据写入换成这样:

BIT_SRAM(bit_union,FLAG_ONE) = 0;		//给第一个flag_1写入0

这样就可以,保证不会因为数错这种低级错误引发不必要的被鄙视。

但是写入需要注意的是,必须要且只能写入0或者1。这个需要一个较为完整的,可靠的设置方式。自己基本上不会犯这种奇怪的错误,但是自己的东西免不了让别人使用,别人保不齐就会传入一个0x02这类的诡异情况。在这里我们也可以聊一下,如果传过来0x02会怎么样?

0x122 位带传错?

位带标注只能传输0或者1,那么我们如果传输2会怎样呢?此处分为两种情况。

扩展位定义其他设定:本人对于IC设计知之甚少,但是也有可能IC设计师会将位带中的未定义的位置设定为其他设置的开关,则可能会出现无法想象的功能故障。

扩展位未定义其他设定: 如果当前扩展位没有设计为其他功能的开关,这个时候传入的是0x02,则可能出现的情况是,当前BIT写入了0,但是其他的位置没有任何用处。

所以,一定要保证当前设置的位带只能写入0和1。

0x222初级使用——安全写入

当然,这个数据的定义就需要一定的保护。也就可以将之前的定义换一下,使用较为安全的方式进行操作:

typedef union
{
    unsigned flag1:1;
    unsigned flag2:1;
    unsigned int all_flag;
}U_BIT_UNION;
U_BIT_STRUCT bit_union;
typedef enum
{
    FLAG_MIN = 0,
    FLAG_ONE,
    FLAG_TWE,
    FLAG_MAX
}E_BIT_UNION;
#define BIT_SRAM(address,bit) __IO (SRAM_BASE+address*32+bit*4)		//设定如此,举个例子,具体的设置需要参考具体的手册
#define ncs_bit_union_bitset(bit)   BIT_SRAM(bit_union,bit) = 1
#define ncs_bit_union_bitreset(bit) BIT_SRAM(bit_union,bit) = 0

这样就可以避免直接对于数据进行写入,用不同的函数将其包裹起来。

当然,可能有人说我这个宏定义函数有一个常见的重入问题,那就将其解决。

#define ncs_bit_union_bitset(bit)   do{BIT_SRAM(bit_union,bit) = 1}while(0)
#define ncs_bit_union_bitreset(bit) do{BIT_SRAM(bit_union,bit) = 0}while(0)

0x322中级使用——防止索引位置错误

当前数据还有一个可能出现的更加有问题的情况,从最初的BIT_SRAM 可以看出,这个参数宏定义很可能出现误传入错误的bit的情况。所以我们也要对其进行一定的设计保证当输入错误的bit时,由程序报错来提醒我们。

typedef union
{
    unsigned flag1:1;
    unsigned flag2:1;
    unsigned int all_flag;
}U_BIT_UNION;
U_BIT_STRUCT bit_union;
typedef enum
{
    FLAG_MIN = 100,
    FLAG_ONE = FLAG_MIN,
    FLAG_TWE,
    FLAG_MAX
}E_BIT_UNION;

#define BIT_SRAM(address,bit) __IO (SRAM_BASE+address*32+bit*4)		//设定如此,举个例子,具体的设置需要参考具体的手册
#define ncs_bit_union_bitset(bit)   do{BIT_SRAM(bit_union,bit) = 1}while(0)
#define ncs_bit_union_bitreset(bit) do{BIT_SRAM(bit_union,bit) = 0}while(0)

这样的定义肯定会导致当前的设置出错。所以需要一个真正的函数对其进行操作。(当然,你想要用宏定义也可以,就是实现的方式不同而已)

void ncs_bitset(void * address,E_BIT_UNION bit)
{
    if((bit<FLAG_MAX)&&(bit>=FLAG_MIN))
    {
        bit -= FLAG_MIN;
        BIT_SRAM(bit_union,bit) = 1;
    }
    else
    {
        printf("error is %d,bitset",bit);
    }
}

void ncs_bitreset(void * address,E_BIT_UNION bit)
{
    if((bit<FLAG_MAX)&&(bit>=FLAG_MIN))
    {
        bit -= FLAG_MIN;
        BIT_SRAM(bit_union,bit) = 0;
    }
    else
    {
        printf("error is %d,bitreset",bit);
    }
}

0x422高级使用——多参数定义保证不会错误传参

当然,我们设置的不可能只有一个BIT位绑定,所以需要很多的设置项来进行操作,就很容易传错误的参数进去。我们在之前的方案可以规避这个故障,但是最好能够在出现问题时,编译器就直接阻止这个问题。

typedef union
{
    unsigned flag1:1;
    unsigned flag2:1;
    unsigned int all_flag;
}U_BIT_UNION;
U_BIT_STRUCT bit_union_1;
U_BIT_STRUCT bit_union_2;
typedef enum
{
    FLAG_1MIN = 100,
    FLAG_1ONE = FLAG_MIN,
    FLAG_1TWE,
    FLAG_1MAX
}E_BIT_UNION_1;

typedef enum
{
    FLAG_2MIN = 200,
    FLAG_2ONE = FLAG_MIN,
    FLAG_2TWE,
    FLAG_2MAX
}E_BIT_UNION_2;

#define BIT_SRAM(address,bit) __IO (SRAM_BASE+address*32+bit*4)		//设定如此,举个例子,具体的设置需要参考具体的手册
#define ncs_bit_union_1_bitset(bit)   do{ncs_bit1focuset(bit,1);}while(0)
#define ncs_bit_union_1_bitreset(bit) do{ncs_bit1focuset(bit,0);}while(0)
#define ncs_bit_union_2_bitset(bit)   do{ncs_bit2focuset(bit,1);}while(0)
#define ncs_bit_union_2_bitreset(bit) do{ncs_bit2focuset(bit,0);}while(0)

void ncs_bit1focuset(E_BIT_UNION_1 address,unsigned int  bit)
{
    if((bit<FLAG_1MAX)&&(bit>=FLAG_1MIN))
    {
        bit -= FLAG_1MIN;
		BIT_SRAM(bit_union_1,address) = bit;
    }
    else
    {
        printf("error is %d,bitset",bit);
    }
}

void ncs_bit2focuset(E_BIT_UNION_2 address,unsigned int  bit)
{
    if((bit<FLAG_2MAX)&&(bit>=FLAG_2MIN))
    {
        bit -= FLAG_2MIN;
        BIT_SRAM(bit_union_2,address) = bit;
    }
    else
    {
        printf("error is %d,bitset",bit);
    }
}

这样如果传输了错误的参数,在很多的编译器中就会识别为警告,这样就可以辅助我们找到这个问题进行修复。

而上述文件都是在同一代码块定义的,所以可能看起来较为冗余。其实实际使用还是较为简单的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bo4L52B7-1579442222967)(https://i.imgur.com/ehE3lkH.png)]

如上可以看出,整个设计的架构都较于完整,也方便别人使用。

更多

本文首发自 位带操作与应用手记-我的博客,更多文章可进入我的博客详查。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GreenDreamer

如果帮到了你,还望请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值