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 = 0;
sleep(); //等待一段时间,栓塞当前进程
……
}
在一般的编译器对于其的处理为:
当前的数据清除和数据写入本身没有什么问题,但是如果出现了竞争的情况,就会出现某个时期,两者都为1或者都为0的情况。因为每次操作,都会将数据取出,再写入,这种操作是非原子(atom)的。所以可能会出现:
这种情况,这种情况对于系统而言是十分不稳定的,因为可能在不可控的时间段内出现异常的清空和置位。
0x21比特栓塞
依托伟大的CS科学家们,他们发明了锁来处理这个情况。因为现代计算机的汇编语言约定俗成的机制为:自增和自减默认为原子操作(单个汇编指令),将锁的初始值设为1,当加锁的时候,仅需要自减即可清零。而读取则直接自加判断是否为一即可。这样就可以合理的栓塞住某些不切实际的修改,导致当前值出现异常的错误值。
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)]](https://i-blog.csdnimg.cn/blog_migrate/841dc63ba6431d8a948a6832bea05f0f.png)
如上可以看出,整个设计的架构都较于完整,也方便别人使用。
更多
本文首发自 位带操作与应用手记-我的博客,更多文章可进入我的博客详查。

3906

被折叠的 条评论
为什么被折叠?



