Programmable Devices
CPLDs, FPGAs, SoC FPGAs, Configuration, and Transceivers
21611 Discussions

custom peripheral memory-mapping issue

Altera_Forum
Honored Contributor II
2,099 Views

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
0 Kudos
4 Replies
Altera_Forum
Honored Contributor II
838 Views

try using signaltap

0 Kudos
Altera_Forum
Honored Contributor II
838 Views

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 :)
0 Kudos
Altera_Forum
Honored Contributor II
838 Views

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.
0 Kudos
Altera_Forum
Honored Contributor II
838 Views

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

0 Kudos
Reply