Hi I have a question regarding to interfacing with narrower or wider width of slave. As we know, our Nios II is 32-bit width. To perform a read/write to a slave, says a 16-bit memory, how should this be done in software using IO macros? FYI, my 16-bit memory controller can be either the ssram controller or sdram controller available in SOPC Builder or Qsys. Thanks.
The SOPC builder will include a 'bus width adapter' between the nios cpu and any 16bit slave. This automatically generates two 16bit slave cycles for each master cycle. The code is the same.
Thanks dsl for your quick answer. Do you mean I'm going to use IOWR_32DIRECT(_BASE, 0, 0x12345678);?In fact, I have another project which needs to use my own custom memory controller. Unfortunately, it does not have byte-enable signal. Do you have any advice for me? Thanks again.
IOWR_32DIRECT() probably maps down to the correct instruction :-)If it is 'normal' memory, you'll have to support byte enables - otherwise single byte writes won't work, even for a 16bit write you'll see two writes one will have neither byte enable asserted! For memory mapped io buffers it is usually best to create a 32bit slave that hard-wires d16-31 to zero for reads.
In your Nios bsp project, check outHAL/inc/io.h
/* Dynamic bus access functions */ #define __IO_CALC_ADDRESS_DYNAMIC(BASE, OFFSET) \ ((void *)(((alt_u8*)BASE) + (OFFSET))) #define IORD_32DIRECT(BASE, OFFSET) \ __builtin_ldwio (__IO_CALC_ADDRESS_DYNAMIC ((BASE), (OFFSET))) #define IORD_16DIRECT(BASE, OFFSET) \ __builtin_ldhuio (__IO_CALC_ADDRESS_DYNAMIC ((BASE), (OFFSET))) #define IORD_8DIRECT(BASE, OFFSET) \ __builtin_ldbuio (__IO_CALC_ADDRESS_DYNAMIC ((BASE), (OFFSET))) #define IOWR_32DIRECT(BASE, OFFSET, DATA) \ __builtin_stwio (__IO_CALC_ADDRESS_DYNAMIC ((BASE), (OFFSET)), (DATA)) #define IOWR_16DIRECT(BASE, OFFSET, DATA) \ __builtin_sthio (__IO_CALC_ADDRESS_DYNAMIC ((BASE), (OFFSET)), (DATA)) #define IOWR_8DIRECT(BASE, OFFSET, DATA) \ __builtin_stbio (__IO_CALC_ADDRESS_DYNAMIC ((BASE), (OFFSET)), (DATA)) /* Native bus access functions */ #define __IO_CALC_ADDRESS_NATIVE(BASE, REGNUM) \ ((void *)(((alt_u8*)BASE) + ((REGNUM) * (SYSTEM_BUS_WIDTH/8)))) #define IORD(BASE, REGNUM) \ __builtin_ldwio (__IO_CALC_ADDRESS_NATIVE ((BASE), (REGNUM))) #define IOWR(BASE, REGNUM, DATA) \ __builtin_stwio (__IO_CALC_ADDRESS_NATIVE ((BASE), (REGNUM)), (DATA))I think the "16" macros variants eliminate double reads and writes to a 16 bit slave. Like dsl said, you will still need to use byteeanble signals to control byte-wide writes (if you want to use IOWR_8DIRECT). I also think that IOWR_32DIRECT will actually give you two writes to a 16 bit slave, with the two byteenables ALWAYS asserted. That just makes more sense to me.
The double read behavior depends on the tools and IP being accessed.In SOPC Builder if you perform a 32-bit access in your code to a 16-bit slave two things could happen: Slaves that uses native addressing (no byte enables) --> a single access occurs and the upper 16-bits drop off during writes or get zero padded on reads. Slaves that use dynamic addressing (byte enables) --> two accesses occur at the two 16-bit locations being accessed. In Qsys there is only dynamic addressing so any native slave gets treated as a 32-bit slave port and the accesses behave as if there were byte enables present in the slave. So in that case if your slave port was native and 16-bit and Nios II performs a 32-bit access you'll end up with two accesses regardless if the slave used native addressing or not. At the end of the day my advice is to avoid native addressing at all costs since the behavior changes in Qsys and native addressing also becomes limiting in cases where the master and slave do not match in width (like a 64 bit master accessing a 32 bit slave).