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

Avalon MM slave custom component: register reading/writing issue

Altera_Forum
Honored Contributor II
3,235 Views

Hi all! 

 

I'm having some trouble designing an Avalon MM slave component. I’m new with FPGA and probably is something really easy to fix. I’m using Quartus prime (15.1) and a Max10 FPGA development kit. 

 

I’d like to understand the right way to implement a custom Avalon MM salve component and to integrate it into Qsys to read/write registers from NIOS processor.  

 

At moment I can write and read the registers from the processor but something strange is happening into the memory. The component is using 8 bits address and 4 test registers LED0, LED1, LED2, LED3. When I try to write the registers with 4 different values it seems that the values are shifted by 1 location (a word) and the last value is written in all the rest of the memory space allocated to the component. 

 

The component address assigned in Qsys is 0x9400 and the picture attached shows the content of the memory in debug mode in Eclipse. Verilog code and C code are also attached. 

 

Does anybody have a good idea about a solution? 

 

 

Thanks 

 

Verilog component code: 

 

// testSlave.v 

 

`timescale 1 ps / 1 ps 

 

module testSlave ( 

input [7:0] avs_testSlave_address, // testslave.address 

input avs_testSlave_read, // .read 

output [31:0] avs_testSlave_readdata, // .readdata 

input avs_testSlave_write, // .write 

input [31:0] avs_testSlave_writedata, // .writedata 

output avs_testSlave_waitrequest, // .waitrequest 

input clock_testSlave_clk, // clock_testSlave.clk 

input reset_testSlave_reset // reset_testSlave.reset 

); 

 

reg [31:0] LED0,LED1,LED2,LED3;  

reg [31:0] avs_testSlave_readdata_; 

 

assign avs_testSlave_readdata = avs_testSlave_readdata_; 

 

 

always @(posedge clock_testSlave_clk or posedge reset_testSlave_reset) 

 

begin //write 

 

if(reset_testSlave_reset == 1) //if(!reset_testSlave_reset) 

begin 

LED0 <= 0; 

LED1 <= 0; 

LED2 <= 0; 

LED3 <= 0;  

end 

 

else if(avs_testSlave_write) 

begin 

 

case(avs_testSlave_address) 

0: begin LED0 <= avs_testSlave_writedata; end 

1: begin LED1 <= avs_testSlave_writedata; end 

2: begin LED2 <= avs_testSlave_writedata; end 

3: begin LED3 <= avs_testSlave_writedata; end 

endcase 

 

end 

 

end 

 

 

always @(posedge clock_testSlave_clk or posedge reset_testSlave_reset) 

 

begin //read 

 

if (reset_testSlave_reset == 1) 

begin 

avs_testSlave_readdata_ <= 0; 

end 

 

else if(avs_testSlave_read) 

begin 

 

case(avs_testSlave_address) 

0: begin avs_testSlave_readdata_ <= LED0; end 

1: begin avs_testSlave_readdata_ <= LED1; end 

2: begin avs_testSlave_readdata_ <= LED2; end 

3: begin avs_testSlave_readdata_ <= LED3; end 

endcase 

 

end 

 

end 

 

endmodule 

 

Nios C code: 

 

while(1) 

if(alt_timestamp() > 50000000) 

printf("* %u \n", alt_timestamp()); 

printf("* %u \n", counter); 

counter++; 

alt_timestamp_start(); 

 

if(counter %2 == 0) 

testValue = 0x0A; 

IOWR(TESTSLAVE_0_BASE, 0, testValue); 

printf("\n write0 %u \n", testValue); 

testValue = 0x0B; 

IOWR(TESTSLAVE_0_BASE, 1, testValue); 

printf("\n write1 %u \n", testValue); 

testValue = 0x0C; 

IOWR(TESTSLAVE_0_BASE, 2, testValue); 

printf("\n write2 %u \n", testValue); 

testValue = 0x0D; 

IOWR(TESTSLAVE_0_BASE, 3, testValue); 

printf("\n write3 %u \n", testValue); 

 

else 

ledRegValue0 = IORD(TESTSLAVE_0_BASE, 0); 

printf("\n read led0 %u \n", (ledRegValue0)); 

ledRegValue1 = IORD(TESTSLAVE_0_BASE, 1); 

printf("\n read led1 %u \n", (ledRegValue1)); 

ledRegValue2 = IORD(TESTSLAVE_0_BASE, 2); 

printf("\n read led2 %u \n", (ledRegValue2)); 

ledRegValue3 = IORD(TESTSLAVE_0_BASE, 3); 

printf("\n read led3 %u \n", (ledRegValue3)); 

 

http://www.alteraforum.com/forum/attachment.php?attachmentid=11459&stc=1  

 

0 Kudos
14 Replies
Altera_Forum
Honored Contributor II
1,585 Views

Hello, 

 

Few points from my side : 

 

(1) Offsets which are provided as arguments in IOWR/IORD functions are byte offsets. 

 

(2) Data width for your component (width for read data and write data) is 32 bits. I request you to read description of 'address' from Table 3-1: Avalon-MM Signal Roles, Avalon Specification. Also read description about 'addressUnits' parameter from that specification. 

Thus, for your code, 

register -> byte offset (to be used in iowr/iord) -> avalon slave offset(to be used in verilog file) 

LED0 -> 0x0 -> 0x0 

LED0 -> 0x4 -> 0x1 

LED0 -> 0x8 -> 0x2 

LED0 -> 0xC -> 0x3 

 

In short, if you wish to write to LED2, you should use following : 

IOWR(TESTSLAVE_0_BASE, 0x8, testValue); 

Similarly for LED3, 

IOWR(TESTSLAVE_0_BASE, 0xC, testValue); 

 

 

Cheers, 

Bhaumik
0 Kudos
Altera_Forum
Honored Contributor II
1,585 Views

Bhaumik is correct only if you have set the addressing as byte, your current addressing is correct if your tcl file specifies word addressing. 

 

The issue is you are not generating a the wait signal correctly for your read data implementation. Since you do not generate wait at all it will be connected as a 0 and therefor data must be ready in the same clock as the read command is issued. Since your data is registered you must COMBINATIONALLY generate a wait=1 for the clock that the read is issued and then deassert wait the next clock.
0 Kudos
Altera_Forum
Honored Contributor II
1,585 Views

Hi Bhaumik, 

 

thanks for replying. 

 

I did the following changes : 

 

 

testValue = 0x0A; 

IOWR(TESTSLAVE_0_BASE, 0x00, testValue); 

printf("\n write0 %u \n", testValue); 

testValue = 0x0B; 

IOWR(TESTSLAVE_0_BASE, 0x04, testValue); 

printf("\n write1 %u \n", testValue); 

testValue = 0x0C; 

IOWR(TESTSLAVE_0_BASE, 0x08, testValue); 

printf("\n write2 %u \n", testValue); 

testValue = 0x0D; 

IOWR(TESTSLAVE_0_BASE, 0x0C, testValue); 

printf("\n write3 %u \n", testValue); 

 

and 

 

ledRegValue0 = IORD(TESTSLAVE_0_BASE, 0x00); 

printf("\n read led0 %u \n", (ledRegValue0)); 

ledRegValue1 = IORD(TESTSLAVE_0_BASE, 0x04); 

printf("\n read led1 %u \n", (ledRegValue1)); 

ledRegValue2 = IORD(TESTSLAVE_0_BASE, 0x08); 

printf("\n read led2 %u \n", (ledRegValue2)); 

ledRegValue3 = IORD(TESTSLAVE_0_BASE, 0x0C); 

printf("\n read led3 %u \n", (ledRegValue3)); 

 

and the NIOS II console is printing: 

 

read led0 10 

read led1 10 

read led2 10 

read led3 10 

 

So, still not working. To be honest I did this attempt before posting this issue and I knew the result...  

Any other idea? 

 

Thanks, 

Dario
0 Kudos
Altera_Forum
Honored Contributor II
1,585 Views

Hi Necare, 

 

this totaly makes sense and can explain why the reading is shifted by one. Also I'm trying to read continuosly just one register and it works fine after the first cycle. Do you have any idea about the reason why the other component locations are filled with 0x0D000000? (please refer to the previous screenshot) 

 

Thanks, 

Dario
0 Kudos
Altera_Forum
Honored Contributor II
1,585 Views

The first D is likely because your code ran and thus loaded the value of LED3 into the register before the dump.  

 

The value of LED3 is in all other locations because your code doesn't update the read data register for any other addresses. If your case statement was sized to the lower 2 bits it would cycle between all 4 values or you could put a "default:" case to use for all other addresses. 

 

You could prove this by reading a valid address(ie 9404) once then continuously reading invalid addresses and get the value you expected at that address for all of them (ie 0x0B)
0 Kudos
Altera_Forum
Honored Contributor II
1,585 Views

Hi guys, 

 

thank you very much for your help! The component is working fine now. I referred also to Avalon Interface specification page 3-12. 

I'm attaaching a copy of the working verilog code, just in case somebody need it :) Cheers 

 

// testSlave.v 

 

`timescale 1 ps / 1 ps 

 

module testSlave ( 

input [7:0] avs_testSlave_address, // testslave.address 

input avs_testSlave_read, // .read 

output [31:0] avs_testSlave_readdata, // .readdata 

input avs_testSlave_write, // .write 

input [31:0] avs_testSlave_writedata, // .writedata 

output avs_testSlave_waitrequest, // .waitrequest 

input clock_testSlave_clk, // clock_testSlave.clk 

input reset_testSlave_reset // reset_testSlave.reset 

); 

 

 

 

reg [31:0] LED0,LED1,LED2,LED3;  

reg [31:0] avs_testSlave_readdata_reg; 

reg waitRequest; 

 

assign avs_testSlave_readdata = avs_testSlave_readdata_reg; 

assign avs_testSlave_waitrequest = avs_testSlave_read & waitRequest; 

 

 

 

always @(posedge clock_testSlave_clk or posedge reset_testSlave_reset) 

 

begin //write 

 

if(reset_testSlave_reset == 1) //if(!reset_testSlave_reset) 

begin 

LED0 <= 0; 

LED1 <= 0; 

LED2 <= 0; 

LED3 <= 0;  

end 

 

else if(avs_testSlave_write) 

begin 

 

case(avs_testSlave_address) 

0: begin LED0 <= avs_testSlave_writedata; end 

1: begin LED1 <= avs_testSlave_writedata; end 

2: begin LED2 <= avs_testSlave_writedata; end 

3: begin LED3 <= avs_testSlave_writedata; end 

endcase 

 

end 

 

end 

 

 

always @(posedge clock_testSlave_clk or posedge reset_testSlave_reset) 

 

begin //read 

 

if (reset_testSlave_reset == 1) 

begin 

avs_testSlave_readdata_reg <= 0; 

waitRequest <= 1; 

end 

 

else if(avs_testSlave_read == 1) 

begin  

waitRequest <= 0; 

 

case(avs_testSlave_address) 

0: begin avs_testSlave_readdata_reg <= LED0; end 

1: begin avs_testSlave_readdata_reg <= LED1; end 

2: begin avs_testSlave_readdata_reg <= LED2; end 

3: begin avs_testSlave_readdata_reg <= LED3; end 

default: begin avs_testSlave_readdata_reg <= 0; end 

endcase 

end 

 

else if(avs_testSlave_read == 0) 

begin 

waitRequest <= 1; 

end 

 

end 

 

endmodule 

0 Kudos
Altera_Forum
Honored Contributor II
1,585 Views

Hi Guys, 

 

the component is working fine now, thanks. I referred also to Avalon interface specification page 3-12. I'm attaching the working verilog file, just in case someboby need it.  

 

Cheers
0 Kudos
Altera_Forum
Honored Contributor II
1,585 Views

Hello Dario, 

 

Thank you for posting your Verilog code here. If you do not mind, I request you to post here HW_TCL file for that component as well. Both Verilog file and HW_TCL file together would make it much easier for somebody else to take reference from that. 

 

Cheers, 

Bhaumik
0 Kudos
Altera_Forum
Honored Contributor II
1,585 Views

Hi guys, 

 

Attached you can find the verilog file and the tcl. The .v file is slightly different but I think more useful. I added 4 exported signals to manage 4 PWMs. 

Please change file extension .txt to .tcl
0 Kudos
Altera_Forum
Honored Contributor II
1,585 Views

Hi, Thanks for the above, very helpful. 

 

But I am still having trouble outputting data from a bidirectional PIO. 

I am working with a DE0-Nano-SoC, in C in Linux, and have created 2 output only PIOs, 

which work fine. 

 

I also have a bidirectional data PIO that can read readonly registers in my firmware,  

but if I write data to read/write registers in my firmware, and then read it back, it is always 0. 

Tracing with SignalTap shows no data being output from the PIO on write. 

 

Below is the crucial code, I am hoping someone can see where I am going wrong. 

Is it the address calculation for the Data Direction register ? 

 

This is for the Data register (which works for the output only PIOs) : 

h2p_lw_pio_d_addr=virtual_base + ( ( unsigned long )( ALT_LWFPGASLVS_OFST + PIO_D_BASE ) & ( unsigned long)( HW_REGS_MASK ) ); 

 

Is this right for the Data Direction register ? 

h2p_lw_pio_d_dirn = virtual_base + ( ( unsigned long )( ALT_LWFPGASLVS_OFST + PIO_D_BASE + 1 ) & ( unsigned long)( HW_REGS_MASK ) ); 

 

https://alteraforum.com/forum/attachment.php?attachmentid=14661&stc=1  

 

I am setting this as : 

Write : 

Set Data (8 bit) :  

Data = strtol(argv[4], &strptr, 10); // Data to write 

*(uint32_t *)h2p_lw_pio_d_addr = Data; 

Output it : 

*(uint32_t *)h2p_lw_pio_d_dirn = 0xff ; // All PIO Data bits set to output (write) 

 

 

Set Input : 

*(uint32_t *)h2p_lw_pio_d_dirn = 0x00 ; // All PIO Data bits set to input (read) 

Read Data : 

SkelData_read = *(uint32_t *)h2p_lw_pio_d_addr; 

 

Thanks, 

Beau Webber
0 Kudos
Altera_Forum
Honored Contributor II
1,585 Views

Try it like this: 

 

h2p_lw_pio_d_dirn = virtual_base + ( ( unsigned long )( ALT_LWFPGASLVS_OFST + PIO_D_BASE + 4 ) & ( unsigned long)( HW_REGS_MASK ) ); 

 

The gpio_swporta_ddr (data direction) register offset is 0x4, not 0x1, according to cv_5_hps_trm.pdf manual.
0 Kudos
Altera_Forum
Honored Contributor II
1,585 Views

 

--- Quote Start ---  

Try it like this: 

 

h2p_lw_pio_d_dirn = virtual_base + ( ( unsigned long )( ALT_LWFPGASLVS_OFST + PIO_D_BASE + 4 ) & ( unsigned long)( HW_REGS_MASK ) ); 

 

The gpio_swporta_ddr (data direction) register offset is 0x4, not 0x1, according to cv_5_hps_trm.pdf manual. 

--- Quote End ---  

 

 

Yes indeed you are quite correct, that works fine, thank you very much. 

I can now read and write the registers in my firmware. 

I understand the 4 vs 1 is a matter of byte vs word addressing. 

I now have 4 of the "instruments" from my Cyclone II firmware installed in the DE0-Nano-SoC fabric, and under test. 

Sorry to be a pain, but can you give me a link to the cv_5_hps_trm.pdf manual ? I have searched the Altera site and not yet found it. 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

.
0 Kudos
Altera_Forum
Honored Contributor II
1,585 Views

Is it a Cyclone IV device on the DE0-Nano board? 

 

I used the Cyclone V manual since that's what I have available in my board (DE10-Nano). 

The Cyclone V HPS Technical Reference manual is here: 

https://www.altera.com/documentation/sfo1410143707420.html 

Page 22-4, GPIO Module Address Map. 

 

Try here to search for the corresponding Cyclone IV manual: 

https://www.altera.com/products/fpga/cyclone-series/cyclone-iv/support.html
0 Kudos
Altera_Forum
Honored Contributor II
1,585 Views

Oh thanks indeed. 

I am using the DE0-Nano-SoC board, which has a Cyclone V : 

 

Programmable Logic IC Development Tools Cyclone V SE 5CSEMA4U23C6N + 800MHz Dual-core ARM Cortex-A9 processor 

http://www.terasic.com.tw/cgi-bin/page/archive.pl?language=english&categoryno=205&no=941  

https://www.mouser.co.uk/new/terasic-technologies/terasic-atlas-soc-kit/ 

 

Except for the point that you have helped me over I have found it a very easy and flexible system to work with. 

The two rows of GPIO connectors map well to similar double rows of connectors on my Cyclone II modules, and is allowing me to port hardware as well as the firmware.
0 Kudos
Reply