IOMEM模块是定义的一个AXI总线上的一块Memory Map的资源,所以它有一个base,有一个size。
如果不定义具体的操作函数,那么对IOMEM的操作,就只能用最基本的“=”操作,配合volatile指针,将常数地址进行强制转换来完成,这并不符合C++的接口封装的设计思想。
为了增强可读性,需要为IOMEM设计一系列配套的操作函数。
这里,将IOMEM封装为class,而不是struct。
class IoMem
{
public:
IoMem() {}
IoMem(u32 phy_base, u32 size);
void set_value(u32 offset, u32 value, int flag = 1);
void set_value(u32 offset, int value, int flag = 1);
void set_value(u32 offset, float value, int flag = 1);
void set_value(u32 offset, u32 value, u32 mask, u32 shift, int flag = 1);
u32 get_value(u32 offset, int flag = 1);
int get_value_as_int(u32 offset, int flag = 1);
float get_value_as_float(u32 offset, int flag = 1);
void set_bits_value(u32 offset, u32 mask, u32 shift, int value, int flag = 1);
void set_bit_value(u32 offset, u32 mask, int value, int flag = 1);
void set_bit(u32 offset, u32 mask, int flag = 1);
void clr_bit(u32 offset, u32 mask, int flag = 1);
u32 float_to_u32(float value);
float u32_to_float(u32 value);
public:
u32 phy_base_;
u32 total_size_;
};
#define BIT(nr) (1 << (nr))
#define GENMASK(h, l) ((((1) << ((h) - (l) + 1)) - 1) << (l))
在操作集中,我们看到,
set_value被重载了4次,
这是为了适应不同的输入参数,
但是其他的函数,并没有重载,是为了增强可读性,
而且,返回值的类型,并不能判决重载。
来看CPP文件中的定义,
void IoMem::set_value(u32 offset, u32 value, int flag)
{
u32 addr = phy_base_ + offset * 4;
Xil_Out32(addr, value);
if (flag) {
plog("set value: 0x%08x 0x%08x\r\n", addr, value);
}
}
这个是最基础的set_value函数,其他函数都要调用它的服务,或者说,其他函数是对它的封装。
注意,AXI总线地址是BYTE地址,所以u32占4个BYTE,这里给出的offset,是以REG为单位给出的,即u32,所以在用AXI总线去传输的时候,地址要左移两位,即乘以4。
void IoMem::set_value(u32 offset, int value, int flag)
{
set_value(offset, (u32)value, flag);
}
void IoMem::set_value(u32 offset, float value, int flag)
{
set_value(offset, float_to_u32(value), flag);
}
void IoMem::set_value(u32 offset, u32 value, u32 mask, u32 shift, int flag)
{
u32 result = get_value(offset, flag);
result &= (~mask);
result |= value << shift;
set_value(offset, result, flag);
}
再来看看get_value系列,
u32 IoMem::get_value(u32 offset, int flag)
{
u32 addr = phy_base_ + offset * 4;
u32 result = Xil_In32(addr);
if (flag) {
plog("get value: 0x%08x 0x%08x\r\n", addr, result);
}
return result;
}
int IoMem::get_value_as_int(u32 offset, int flag)
{
u32 result = get_value(offset, flag);
return (int)result;
}
float IoMem::get_value_as_float(u32 offset, int flag)
{
u32 result = get_value(offset, flag);
return u32_to_float(result);
}
再来看看BIT操作系列,
void IoMem::set_bit(u32 offset, u32 mask, int flag)
{
u32 result = get_value(offset, flag);
result |= mask;
set_value(offset, result, flag);
}
void IoMem::clr_bit(u32 offset, u32 mask, int flag)
{
u32 result = get_value(offset, flag);
result &= (~mask);
set_value(offset, result, flag);
}
void IoMem::set_bit_value(u32 offset, u32 mask, int value, int flag)
{
if (value)
set_bit(offset, mask, flag);
else
clr_bit(offset, mask, flag);
}
void IoMem::set_bits_value(u32 offset, u32 mask, u32 shift, int value, int flag)
{
u32 result = get_value(offset, flag);
result &= ~mask;
result |= value << shift;
set_value(offset, result, flag);
}
再来看看类型转换操作系列,
u32 IoMem::float_to_u32(float value)
{
u32 tmp;
tmp = (U32)value;
return tmp;
}
float IoMem::u32_to_float(u32 value)
{
float tmp;
tmp = (float)value;
return tmp;
}