最近对一段声卡裸板程序进行调试时发现了一个结构体对齐误用的错误。在接有wm8976的s3c2440开发板上调试裸板程序,用来解析并播放NandFlash上的wav二进制数据,使用dma传输数据到iis接口,并在传输完毕后触发中断再次传输。然而结果是既没有声音,dma中断也没有被触发,以为是dma的设置出错,经过反复试验后,将问题定位到了iis的部分代码:
struct i2s_regs
{
unsigned long iiscon;
unsigned long iismod;
unsigned long iispsr;
unsigned long iisfcon;
unsigned long iisfifo;
}__attribute__((packed));
static volatile struct i2s_regs* i2sregs;
void i2s_init(unsigned int bits_per_channel,unsigned int samp_per_second)
{
int i;
int min = 0xffff;
unsigned int tmp_fs;
unsigned int pre = 0;
i2sregs = (struct i2s_regs*)0x55000000;
GPECON &= ~0x3ff;
GPECON |= 0x2aa;
if(bits_per_channel == 16)
{
i2sregs->iismod = (2<<6) | (1<<3) | (1<<2) | (1);
}else{
i2sregs->iismod = (2<<6) | (0<<3) | (1<<2) | (1);
}
for(i = 0;i < 32;i++)
{
tmp_fs = PCLK/384/(i+1);
if(ABS(tmp_fs,samp_per_second) < min)
{
min = ABS(tmp_fs,samp_per_second);
pre = i;
}
}
i2sregs->iispsr = (pre<<5) | pre ;
i2sregs->iisfcon = (1<<15) | (1<<13) ;
i2sregs->iiscon = (1<<5) | (1<<1);
}
将结构体指针指向iis寄存器的首地址,通过该结构体指针来访问设置iis寄存器,去掉attribute__ ((packed)) 的属性描述符后成功运行了程序,那么为什么会出现这种问题呢?
因为attribute__ ((packed)) 的属性描述符的作用是不进行结构体成员的对齐,在内存中按成员的大小依次排列各成员,按理说 这五个成员都是unsigned long 类型,本身就是4字节对齐的,无论加不加上attribute__ ((packed)) 的属性描述符编译出来的结果都应该是一样的,也就是说结构体的所占大小应该都是20,加上打印语句后也是20。那么会不会是指针赋值的时候出了问题呢?i2sregs = (struct i2s_regs*)0x55000000;寄存器的首地址本身也是4字节对齐的,也不会出错,对每个结构体成员进行取址,得到的结果也都和芯片手册上写的一致,那么说明结构体指针映射没有问题,只可能是对它们进行赋值的时候出了问题了,这种情况下也只有查看反汇编的代码来看看究竟了。使用arm-linux-objdump对elf进行反汇编,分别得到加上属性描述符与不加的代码,来进行分析。
这是不加上attribute__ ((packed) 属性描述符的代码,忽略掉值的计算等指令,只寻找最后赋值的指令可以大致发现,对每个结构体成员(寄存器)进行最后的写入时,使用的都是str指令。
33f80860 <i2s_init>:
33f80860: e92d41f0 push {
r4, r5, r6, r7, r8, lr}
33f80864: e3a03455 mov r3, #1426063360 ; 0x55000000
33f80868: e59f80d0 ldr r8, [pc, #208] ; 33f80940 <i2s_init+0xe0>
33f8086c: e1a04000 mov r4, r0
33f80870: e1a06001 mov r6, r1
33f80874: e59f00c8 ldr r0, [pc, #200] ; 33f80944 <i2s_init+0xe4>
33f80878: e1a01003 mov r1, r3
33f8087c: e5883000 str r3, [r8]
33f80880: eb000279 bl 33f8126c <printf>
33f80884: e3a01456 mov r1, #1442840576 ; 0x56000000
33f80888: e5913040 ldr r3, [r1, #64]
33f8088c: e3c33fff bic r3, r3, #1020 ; 0x3fc
33f80890: e3c33003 bic r3, r3, #3 ; 0x3