Search code examples
cnios

int* and char* to write into memory


I recently learnt nios II SOPC, and I encountered the process of writing into and reading from memory. The use of pointer int* and char* lets me have two different results. The code is as follow.

#include "system.h"
#include "stdio.h"

int main()
{
    char* n = (char*) MEMORY_0_BASE; //the address for first block of memory
    int i;

    for(i=0;i<16;i++)
    {
        *(n+i)=i;
    }

    for(i=0;i<16;i++)
    {
        printf("Du lieu tai o nho thu %d la %d\n", i , *(n+i));
    }

    while(1);

}

The code for "int*" is as follow

#include "system.h"
#include "stdio.h"

int main()
{
    char* n = (char*) MEMORY_0_BASE; //the address for first block of memory
    int i;

    for(i=0;i<16;i++)
    {
        *(n+i)=i;
    }

    for(i=0;i<16;i++)
    {
        printf("Du lieu tai o nho thu %d la %d\n", i , *(n+i));
    }

    while(1);

}

The result for using "int*" is 0,1,2,...,15 while the result for "char*" is 3,3,3,3,7,7,7,7,11,11,11,11,15,15,15,15. I cannot explain why this is the case.

The following is my code for the memory block

module memory 
#(parameter DATA_WIDTH=32, parameter ADDR_WIDTH=4)
(
    input iClk, iReset_n,
    input iRead_n, iWrite_n,
    input [ADDR_WIDTH-1:0] iAddress,
    input [DATA_WIDTH-1:0] iData,
    output [DATA_WIDTH-1:0] oData
);

reg [DATA_WIDTH-1:0] mem [2**ADDR_WIDTH-1:0];
reg [ADDR_WIDTH-1:0] addr_reg;

always@(posedge iClk)
begin

    if(!iWrite_n)
        mem[iAddress] = iData;

    if(!iRead_n)
        addr_reg = iAddress;

end


assign oData = mem[addr_reg];

endmodule

Solution

  • This is my speculation based on the observed behaviour.

    Your memory device consists of 16 x 32-bit integers. My speculation is that the compiler which likes to address things in bytes is effectively masking out the lowest two bits of the address. The zeroth register has the address MEMORY_0_BASE + 0 * 4, the first register has the address MEMORY_0_BASE + 1 * 4, the second register has the address MEMORY_0_BASE + 2 * 4 etc.

    If you store ints to the registers using an int pointer, each time you increment the int pointer, the C pointer arithmetic actually adds sizeof(int) = 4 to the address in the pointer, so the sequence of addresses to which ints are stored is as above.

    If you store chars using a char pointer, each time you increment the char pointer, the C pointer arithmetic adds sizeof(char) = 1 to the address in the pointer. The code attempts to store the first four chars (0, 1, 2, 3) to MEMORY_0_BASE + 0, MEMORY_0_BASE + 1, MEMORY_0_BASE + 2, MEMORY_0_BASE + 3. If, as I believe, the bottom two bits of the pointer are being masked, all of those addresses store to MEMORY_0_BASE and, by the time you are done, the value in it is 3.

    Similarly for the second four chars (4, 5, 6, 7). They get stored to MEMORY_0_BASE + 4, MEMORY_0_BASE + 5, MEMORY_0_BASE + 6, MEMORY_0_BASE + 7, which all map to MEMORY_0_BASE + 4 after masking, leaving it containing the number 7 and so on.

    That's how you get the sequence 3,3,3,3,7,7,7,7,11,11,11,11,15,15,15,15.