Intel® Quartus® Prime Software
Intel® Quartus® Prime Design Software, Design Entry, Synthesis, Simulation, Verification, Timing Analysis, System Design (Platform Designer, formerly Qsys)
17267 Discussions

Read/Write Access in Nios II

Altera_Forum
Honored Contributor II
4,287 Views

Hi There,  

 

I'm running a Nios II-based system that is made of the following :  

 

- A 32_bit Nios II CPU (Standard version) 

- An On-chip Mem 

- An EPCS controller 

- A JTAG_uart 

- A timer 

- A custom SOPC component# 1 

- A custom SOPC component# 2 

 

Here is the address mapping : 

0x00231800 - 0x00231fff0x00220000 - 0x00229fff0x00231000 - 0x002317ff0x00232020 - 0x002320270x00232000 - 0x0023201f0x00200000 - 0x0020ffff0x00100000 - 0x001fffff 

 

My C-code in Nios II IDE is very simple. 

int main () { st_AHW_ahw* ahw_ptr = NULL; printf("Hello from Nios II!\n"); //Appel à la fonction Ecriiture code_error = AHW_Write_access (ahw_ptr,0x00100008, 0x0, 0xDECABABE); code_error = AHW_Read_access (ahw_ptr,0x00100008, 0x0); code_error = AHW_Write_access (ahw_ptr,0x00200006, 0x0, 0xBEEFBABE); return 0; } 

 

It is basically 3 accesses that target my two customs components with C-functions of my own. 

 

My question is related to the ModelSim simulation of this piece of code.  

 

I observe theses accesses on my waveform file at very random time when simulating, the first one happens at 2.9ms, the second one at 3.5 ms and the last one at 6.0ms. And in between I see a lot of memory accesses on the Nios II... how can I predict the moment when my R/W accesses will be processed? 

 

Thank you very much! 

0 Kudos
6 Replies
Altera_Forum
Honored Contributor II
3,134 Views

It is difficult to predict because it depends on the code that you have in your functions. It shouldn't be random though, it should be the same each time you run the simulation. 

Why do you want to predict it?
0 Kudos
Altera_Forum
Honored Contributor II
3,134 Views

It's my bad, I wasn't very clear when I mentionned random timing. 

 

What I meant is that I observed these transactions at very random time, for a GIVEN simulation run.  

 

When I run several simulation sets in a row, these acceses do happen always at the same time from one run to another, which is rather logical.  

 

I want to understand the mechanisms that are at stake when I run my code on the Nios II. 

 

For argument's sake here the code of the functions AHW_Read_Access and AHW_Write_Access: 

 

EXTERN en_ret_code AHW_Read_access( st_AHW_ahw *p_ahw, /* Structure de registres sur laquelle appliquer la fonction */ u_int_32 address_MCU, u_int_32 addr_offset ) { en_ret_code ret = RET_CODE_OK; u_int_32* ptr; //ptr recupere l'addresse MCU + offset ptr = (u_int_32 *)(address_MCU + addr_offset); //dereferencement p_ahw->reg0 = *ptr; printf("Addresse Ecriture Bus Local : %x Valeur : %x \n", address_MCU,p_ahw->reg0); return(ret); } EXTERN en_ret_code AHW_Write_access( st_AHW_ahw *p_ahw, /* Structure de registres sur laquelle appliquer la fonction */ u_int_32 address_MCU, u_int_32 addr_offset, u_int_32 data_to_be_written ) { en_ret_code ret = RET_CODE_OK; u_int_32* ptr; //ptr recupere l'addresse MCU + offset ptr = (u_int_32 *)(address_MCU + addr_offset); //dereferencement *ptr = data_to_be_written; //printf("Addresse Ecriture Bus Local : %x Valeur : %x \n", address_MCU,*ptr); return(ret); }  

 

u_int_XX (8, 16, 32, 64) are just typedefs for unsigned char, signed char, unsigned short etc..
0 Kudos
Altera_Forum
Honored Contributor II
3,134 Views

A lots of cycles are used by the Nios processor for the function calls (registers saved on the stack, jump, stack space allocation...) and function execution (instructions and data fetch, results write...), but the action that probably takes the most time is the I/O generated by the printf() call. 

 

I recommend to use the IO_RD/WR macros to access your component, or to use the alt_remap_uncached() routine if you prefer to use pointers. If you don't, the CPU can read data from its cache rather than your components and you'll have strange results.
0 Kudos
Altera_Forum
Honored Contributor II
3,134 Views

Thanks for the insight Daixiwen. It brings one more question:  

 

You said : 

 

--- Quote Start ---  

 

 

I recommend to use the IO_RD/WR macros to access your component, or to use the alt_remap_uncached() routine if you prefer to use pointers. If you don't, the CPU can read data from its cache rather than your components and you'll have strange results. 

--- Quote End ---  

 

 

How can the CPU read data at the wrong memory location(cache) if the address I store in my pointers specifically target the memory space to which my components are mapped? 

 

In other words, what security these macros/functions bring in terms of addressing? 

 

Thanks
0 Kudos
Altera_Forum
Honored Contributor II
3,134 Views

In the nios II IDE, Click Window->Preferences->NIOS II. Then select the "Generate objdump file". Now recompile your software project. When it finishes it will give you a ".objdump" file which contains a complete listing of the assembly code created for your project. You can find your functions and see what instructions are being executed. This should give you an idea of how many cycles it will take to execute. For example, that little printf statement you've got in there is going to eat up a ton of cycles. Another interesting excercise is to change your software project to compile in "Release" mode instead of "Debug" and watch how the timing changes. 

 

Jake
0 Kudos
Altera_Forum
Honored Contributor II
3,134 Views

The CPU won't read in a wrong location, it will read a cached value. 

The first time you read a register from your component, the CPU will read it and store the value in its data cache. 

The second time you read the same register, the CPU will read the value in its cache rather than accessing your peripheral again. This is a nice feature with external RAM, as access to the cache is a lot faster than an external memory. But for a component it may not be desirable, as the value of the register can change, and the CPU won't see it as long as it reads from it's cache. 

You can force the CPU to bypass the cache and access your component on every access by using the IORD/WR macros or change your pointer with the alt_remap_uncached() routine.
0 Kudos
Reply