Forum Discussion

Altera_Forum's avatar
Altera_Forum
Icon for Honored Contributor rankHonored Contributor
12 years ago

custom peripheral memory-mapping issue

Hello everyone.

It's my first approach with addressing custom peripheral in a nios II based system, so I started developing a simple custom hardware that reads a 32 bit input and gives back "input+1". All seemed to work fine, in the C code I used IOWR_32DIRECT(base, 0, input) and IORD_32DIRECT(base, 0) to query the custom logic; I gave 8 as input and I got 9 as output. So I did the next step :) , writing another peripheral which needs a 64-bit transfer. I know Nios only supports 32-bit transfers, so i chunked the single tranfer into two 32-bit ones, using an address bit to distinguish between the two half-words.

here is the vhdl code (obtained from the first that worked, with some modifications) , I simulated it with modelsim and seems to work fine:

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
-- define interface
entity des_wrapper64 is
port (
-- inputs:
address : IN STD_LOGIC;
chipselect : IN STD_LOGIC;
clk : IN STD_LOGIC;
reset_n : IN STD_LOGIC;
write_n : IN STD_LOGIC;
read_n : IN STD_LOGIC;
writedata : IN UNSIGNED(0 to 31);
-- outputs:
readdata : OUT UNSIGNED(0 to 31)
--d: OUT UNSIGNED(0 to 63)--
);
end entity;
architecture arch of des_wrapper64 is
begin
	process(clk,reset_n)
	variable data:UNSIGNED(0 to 63);
			begin
			if reset_n='0' then
			--nop
			elsif clk='1' and clk'event then
			-- writing
				  if chipselect='1' and write_n='0' then
				      case address is
				        when '0' => data(32 to 63) :=writedata(0 to 31);  --write lsb				          
				        when '1' => data(0 to 31) :=writedata(0 to 31); --write msb
				        when others => readdata<=x"FFFFFFFF";
				      end case;
			    end if;
			-- reading (must send input + 1)
			    if chipselect='1' and read_n='0' then
				      data:=data +1;	
				      case address is
				        when '0' => readdata(0 to 31)<= data(0 to 31);  --read lsb
				        when '1' => readdata(0 to 31)<= data(32 to 63); --read msb
				        when others => readdata<=x"FFFFFFFF";
				      end case;
					end if;
		end if;
		--d<=data;
end process;
end arch;

here is the C code:

//base address (Custom Peripheral)# define CP_ADDR						(0x01001030)
int main()
{
  unsigned int data2writelsb=0xAAAAAAAA;  // two halves of input
  unsigned int data2writemsb=0xBBBBBBBB;
  unsigned int data2readlsb=3;                // two halves of output initialized to some value 
  unsigned int data2readmsb=3;
  IOWR_32DIRECT(CP_ADDR,0,data2writelsb);
  IOWR_32DIRECT(CP_ADDR,1,data2writemsb);
  data2readlsb=IORD_32DIRECT(CP_ADDR,0);
  data2readmsb=IORD_32DIRECT(CP_ADDR,1);
  return 0;
}

The problem is I always have:

data2readlsb=0x00000000;

data2readmsb=0x00000000;

i initialized the two data2read halves to 3 to be sure that IORD works (initializing them to 0 i couldn't see if something is actually read or the IORD doesn't work and the zeroes are simply the initialization value unmodified).

I suppose IORD works because modifying randomly the offset I can get values other than zero, so it is actually reading somewhere in the address space!

I really don't know how to fix this problem...can anyone help me?:confused:

Thanks

Anna

4 Replies

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Problem solved, it was my fault, I didn't understand well how avalon bus addressing works :)

    Since I've read many questions about register addressing in custom peripheral, but no one had been resolved, i will explain what i learned, in a simplified way:

    when you define an address width for your logic, you are choosing how much registers you give to your peripheral;

    for example, a width of 3 bits will lead to a maximum of 8 registers: 000,001,010,011 etc.

    in the vhdl code of the hw, you decide what to do with every address (i.e. adding a case statement, case address is "000" => do something...etc.)

    Since Nios address bus is 32 bit wide, avalon bus transforms this addresses into a 32-bit value in this way:

    as first thing, a range of 32-bit addresses is assigned to your hw starting from the base address, every register is 4 byte wide; for example, with 3 bits address we will have a range given by base address, base+4 bytes, base+8 bytes...etc (up to 8 values in total). Every read operation accesses 4 bytes, so if you want to access the branch of the case statement related to address 001, you must type

    IORD(base,4) that leads to an address of BASE_ADDR+4 bytes, and to access address 002 you must type IORD(base,8)...

    writing IORD(base,0), IORD(base,1) , IORD(base,2) will have the same result of calling IORD(base,4), so to access different addresses and so different registers you must jump by multiples of 4 bytes (1 word in nios terminology).

    Hope it was exausting :)
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Yes, I would always map a C struct onto the device, use an address that bypasses the cache and use normal C struct member operations.

    the IORD() etc marcos are designed to cause grief.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Thanks for your tips! I used IOWR32_DIRECT() and IORD32_DIRECT() that should bypass the cache, and it works :)