1. 端模式简介

端模式(Endian)表示数据在存储器中的存放顺序。大端模式(Big-endian),将数据的低位保存在高地址中,而数据的高位,保存在低地址中。小端模式(Little-endian),将数据的低位保存在内存的低地址中,而数据的高位保存在高地址中。

1.1      端模式对数据字节序的影响

对于内存数据字节序,大端模式高字节(MSB)存放在低地址,低字节(LSB)存放在高地址;小端模式低字节(LSB)存放在低地址,高字节(MSB)存放在高地址。如果将一个32位的整数0x12345678存放到一个整型变量(int)中,这个整型变量采用大端或者小端模式在内存中的存储如图 1.1所示。

图 1.1 大小端模式在内存中的存储

1.2      端模式对处理器寄存器的影响

对于处理器的寄存器而言大小端模式也存在区别。大端处理器,比如PowerPC,将其寄存器的最高位msb(most significant bit)定义为0,最低位lsb(least significant bit)定义为31;而小端处理器正好相反,将其寄存器的最高位定义为31,低位地址定义为0。大小端寄存器位图区别如图 1.2所示。

图 1.2大小端模式CPU的寄存器位图分布

1.3      端模式对数据总线的影响

从数据总线角度而言,大端模式下32位数据总线的msb是第0位,MSB是数据总线的第0~7的字段;而lsb是第31位,LSB是第24~31字段。小端模式下32位总线的msb是第31位,MSB是数据总线的第31~24位,lsb是第0位,LSB是7~0字段。

1.4      端模式转换

网络上的绝大多数协议都采用大端编址方式进行编址,因此在有关网络协议的软件设计中,使用小端方式的处理器需要在软件中处理端模式的转变。

大端处理器采用32位总线与小端外设进行访问时,大端处理器的32位数据总线的第0~7位对应小端外设的第31~24位,第8~15位对应第23~16位,第16~23位对应第15~8位,第24~31位第7~0位。有两种处理方式可以解决此问题:(1)在硬件上将数据线0-31和31-0进行反接;(2)从软件上解决这个问题,在底层读写寄存器函数中,将读/写的数据进行大小端转换。

 

2. SylixOS中对端模式的处理

2.1      大小端模式数据转换

大小端模式数据转换实际就是对字节序进行交换,以32位数据为例,转换接口bswap32就是将高低字节进行转换。

bswap32(uint32_t x)
{
    return  (uint32_t)((((x) & 0x000000ff) << 24) |
                       (((x) & 0x0000ff00) <<  8) |
                       (((x) & 0x00ff0000) >>  8) |
                       (((x) & 0xff000000) >> 24));
}

2.2      外设操作通用接口

SylixOS中对于大小端的操作接口与Linux做了兼容。因为普通的外设基本上都是小端模式,对外设的内存操作可以直接使用图 2.1一组接口进行,这些操作屏蔽了大小端的差异。

图 2.1 通用接口

以writel为例,分析下具体的实现。writel是一个宏,展开后实际上是对内存地址的写操作,关键点是对要写入的数据通过htole32进行了转换。htole32接口通过不同CPU定义的大小端模式,判断是否进行大小端数据转换,如果是大端就调用bswap32进行转换,如果是小端不转换。

#define writel(val, addr)   write32(htole32(val), (addr_t)(addr))#define write32(d, a)      write32_raw(d, a)static LW_INLINE VOID  write32_raw (UINT32  uiData, addr_t  ulAddr){
    KN_IO_WMB();
    *(volatile UINT32 *)ulAddr = uiData;
    KN_IO_WMB();
}

 

 

2.3      外设操作特殊接口

根据不同的CPU形态还定义了一些特殊处理接口,可以指定对大小端设备的操作,具体如图 2.2 特殊接口所示。后缀_le接口代表以小端模式操作,_be代表以大端模式操作。

图 2.2 特殊接口

具体实现跟通用接口类似,都是根据不同CPU的大小端模式与要操作的模式是否相同,判断是否需要进行字节序转换。比如PPC的处理器要写入处理器内部的寄存器,因为寄存器也是以大端模式存储的,可以调用write32_be来实现。