I mapped the lightweight bridge of the Arria10 using:
if( ( m_fileDescriptorLightWeight = open( “/dev/mem”, ( O_RDWR | O_SYNC ) ) ) == GENNERAL_ERROR )
and later there is a call to mmap
mmap( NULL, s_fpgaLightWeightMap.s_HW_REGS_SPAN, ( PROT_READ | PROT_WRITE ),MAP_SHARED, m_fileDescriptorLightWeight, s_fpgaLightWeightMap.s_HW_REGS_BASE );
also, defined a union with int32 and struct that is built from bit fields
for example -
unsigned int startSample :1;
unsigned int sampleReady :1;
unsigned int dataInTest :1;
unsigned int padd0 :5;
unsigned int burstLenght :11;
unsigned int padd1 :1;
unsigned int startAddress :10;
unsigned int padd2 :2;
unsigned int uUint;
for our discussion, lets assume a pointer for the union is pointing to the “beginning” of the m_fileDescriptorLightWeight (in the real application, the union is part of bigger struct with many such unions)
when setting the uUint member via the pointer - all is ok
when setting bit field, we see in SignalTap that it first read the 32 bit value (as expected) but the write value afterward doesnt “remember” the value that was read
for example, setting the uUint to 0x204 and read back - value is 0x204 (as expected)
afterward, setting the ptr->sBit.startAddress to 1 and read the register back should return 0x100204 however, im getting 0x100000
Mapping bitfields onto hardware is a terrible idea… there’s no guarantee of alignment or ordering.
If you are mapping a structure onto hardware, you will almost certainly need to specify the packing with a #pragma or data attribute, otherwise many compilers will insert one or more padding bytes which throws your alignment out.
Always use sized types like uint32_t, uint16_t etc. when accessing hardware, not the builtin ‘unsigned integer’, it’s so much safer. Define appropriate bit numbers, masks and shift counts to access the registers.
had to totally disagree with you - it is not terrible idea, it is what these bridges are meant to.
Yes, you need to specify packing, same as if you want to send c based package of data over tcp/udp. this is very useful layer.
Just to give ideas. The one who does the mapping is the MMU. I ve seen that first 2GB of the address space (RAM and ACP) is tagged as normal memory into the MMU, while the 3rd and 4th GB FPGA and HPS peripherals are tagged as peripherals. You can check the difference in ARM documentation. I remember now for example that L2 can reorder petitions normal memory to speed up but this cannot be done to peripheral space (because it could break an activation sequence of the peripheral provoking a malcfunction of it). I think your the problem could be something like that, related with hardware level.
On the other hand alignment is very important at hardware level. For example. I ve seen DMAC breaking when copying from CPU to FPGA if the address of the buffer you wanna copy is not aligned to 32B or if the source and destiny addresses are not a multiple of the size of the data block to move. I recomend copying data using memcpy function (always faster than manually with = signs and pointers) when copying from FPGA to CPU or viceversa. When accessing directly with = is good approach to use as pointers of the type with the same size as the peripheral you are accessing in the FPGA, and this is ensured with int32_t, int16_t etc. It will ease porting too.