Forum Discussion
IOWR and IORD with PIOs
I have a basic question concerning PIOs. Currently, I am sending 4 8-bit integers over 4 ouput PIOs from NIOS to hardware to do a calculation on them and return another 8-bit integer value. This is achieved as below.
alt_u8 val_1 = 10;
alt_u8 val_2 = 15;
alt_u8 val_3 = 20;
alt_u8 val_4 = 25;
alt_u8 result_val;
IOWR_ALTERA_AVALON_PIO_DATA(DATA_OUT1_BASE , val_1);
.
.
IOWR_ALTERA_AVALON_PIO_DATA(DATA_OUT4_BASE , val_4);
result_val = IORD_ALTERA_AVALON_PIO_DATA(RESULT_IN0_BASE,0);
Now instead of using 4 PIOs of width 8, I want to use 1 PIO of width 32 to send my four integer values. What are the IOWR commands that I should write assuming my new 32-bit PIO base address is DATA_OUT_32_BASE? Also on the hardware side where the calculation is done, the input port will now be a 32-bit input, i.e. [31:0] data_32_in;. How do I separate that input signal into its 4 component integers to use in the calculation?15 Replies
- Altera_Forum
Honored Contributor
Thanks for the explanation. Well, I realize that my method is not possible (at least not straighforward), and it is not my final goal anyway to send data this way to hardware for processing. But it got me to understand a bit about Custom Instructions and a lot more about pointers thanks to Cris :)
- Altera_Forum
Honored Contributor
Well, conceptually you can something like this:
alt_u8 array[8] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; Then *((alt_u32*)array) returns 0x04030201 *((alt_u32*)(array+1)) returns 0x05040302 *((alt_u32*)(array+2)) returns 0x06050403 ... and so on For clarity, since you are not used to handle pointers, consider that *((alt_u32*)(array+n)) is the same as *((alt_u32*)&(array[n])) In practical, I can't remember if Nios requires the 32bit alignment in order to do it as supposed. This depends from the data bus architecture. Being this true or not, you'll have very different performance, because in one case the processor can do the bytes to 32bit packing with a single memory access, while in the other it must still handle the single bytes as you do now. Since you already measure cpu time, you can simply put this in your code and test if you obtain an improvement. - Altera_Forum
Honored Contributor
--- Quote Start --- Then change the code this way:
--- Quote End --- Thank you so much again Cris and with the very good explanation too. I tried your method and this operation happened with less ticks than when using the '<<'. Now I have another problem :) Those small_1 and small_2 arrays have changing values, coming from a bigger array. The 8 values are being collected from as a 'sliding window' as shown in code below.alt_u8 small_1= {1,2,3,4}; alt_u8 small_2= {5,6,7,8}; alt_u32 custom_int_res = 0; custom_int_res = ALT_CI_MYMACRO( *((alt_u32 *)small_1), *((alt_u32 *)small_2) );
But the fact that I am populating the small arrays within the loops requires nios cpu time. Is there instead a smart way again using the pointers to do this? For example, suppose my 3x5 data is as such: 10 20 30 40 50 11 21 31 41 51 12 22 32 42 52 I want the following values in each small array at each loop iteration. at x = 0, small_1 = {10,20,30,11} and small_2 = {31,12,22,32} at x = 1, small_1 = {20,30,40,21} and small_2 = {41,22,32,42} at x = 2, small_1 = {30,40,50,31} and small_2 = {51,32,42,52}# define ROW 3# define COL 5 // 2-D array containing unsigned 8-bit integers alt_u8 array_image = { /* fill in values */}; alt_u8 small_1; alt_u8 small_2; for(y=0; y<(ROW -2); y++) { for(x=0; x<(COL-2); x++) { small_1 = array_image; small_1 = array_image; small_1 = array_image; small_1 = array_image; small_2 = array_image; small_2 = array_image; small_2 = array_image; small_2 = array_image; store_result = ALT_CI_MYMACRO( *((alt_u32 *)small_1), *((alt_u32 *)small_2) ); } } - Altera_Forum
Honored Contributor
I assumed your ALT_CI_MYMACRO parameters were pointers and this was wrong.
From your last post I understand it needs 32bit values. Then change the code this way:
The (alt_u32*) forces compiler to interpret small_1 as a pointer to a alt_u32 value instead of the alt_u8 you defined. Then the * references to the pointed value (which is now seen as u32) Regards Crisalt_u8 small_1= {1,2,3,4}; alt_u8 small_2= {5,6,7,8}; alt_u32 custom_int_res = 0; custom_int_res = ALT_CI_MYMACRO( *((alt_u32 *)small_1), *((alt_u32 *)small_2) ); - Altera_Forum
Honored Contributor
Like you already found out, I was going to suggest that you might still have some other problems with pointers. If you pass a pointer, you have to dereference the pointer in the code/macro/firmware before you can get the info that the pointer points to. You were probably getting the results of the addresses of the data, rather than the values of the data.
- Altera_Forum
Honored Contributor
--- Quote Start --- What result do you get instead of he expected 11? Try with this alternate data: alt_u8 small_1[4]= {0x01,0x02,0x04,0x08}; alt_u8 small_2[4]= {0x10,0x20,0x40,0x80}; With this trick you can easily see which data has been added in the result: every bit set to 1 will indicate a specific addendum. --- Quote End --- Thanks Cris for the handy trick. Well, I found out that no matter what data I had in the array, I was always getting 930 as result. In memory, this appeared as A2 and 03. So I deduced something was definitely wrong with my passing of inputs as pointers. Then I tried passing them as 32-bit values, i.e. using __builtin_custom_inii and
This one gave the expected result of 11! I will keep using the builtin_custom_inii now and next thing is I will try your trick of earlier post with pointers which didn't require any cpu effort. But knowing me, be prepared to hear from me soon as pointers and I will never be friends :)alt_u32 input1_32bits = (small_1<<24) | (small_1<<16) |(small_1<<8) | small_1 ; alt_u32 input2_32bits = (small_2<<24) | (small_2<<16) |(small_2<<8) | small_2 ; custom_int_res = ALT_CI_MYMACRO(input1_32bits ,input2_32bits ); - Altera_Forum
Honored Contributor
What result do you get instead of he expected 11?
Try with this alternate data: alt_u8 small_1[4]= {0x01,0x02,0x04,0x08}; alt_u8 small_2[4]= {0x10,0x20,0x40,0x80}; With this trick you can easily see which data has been added in the result: every bit set to 1 will indicate a specific addendum. - Altera_Forum
Honored Contributor
Thanks for the explanation Donq. I have to master this pointer business quickly! I tried the changes but I am still not successful in what I am trying to do.
Perhaps I am doing something wrong in the custom logic which is as shown below where I am expecting my answer to be 1 + 2 + 3 +5 = 11.alt_u8 small_1= {1,2,3,4}; alt_u8 small_2= {5,6,7,8}; alt_u32 custom_int_res = 0; custom_int_res = ALT_CI_MYMACRO((void *)small_1,(void *)small_2);
Or should I try returning a pointer value instead of integer (i.e. using *__builtin_custom_pnpp )? It's probably a stupid mistake again from me, but I can't figure it out! Any help please? Thanksmodule mycalc_c_instr( dataa, datab, result); input dataa; input datab; output result; assign result=( (dataa + dataa + dataa + datab) ); endmodule - Altera_Forum
Honored Contributor
Pointers and arrays are *almost* the same thing in C.
&(small_1) == ??? // something that ain't what you want. (pointer to a pointer to an array?) &(small_1[0]) == small_1 // small_1 without an index is a pointer to the zero'th item in the array. The [] does much the same thing that the & un-does. also, (small_1+0), (small_1+1), etc. point to individual items in the array, regardless of the size of the items ...so there isn't any need to create a new variable. Try just going ALT_CI_MYMACRO((void *)small_1, ... if you *really* want, you can probably use &(small_1[]), or &(small_1[0]) instead of small_1 to make a point, but that's unnecessary. Then you would have (void *) &(small_1[?])... Array, pointerize, typecast ... whew! Sort of like if (!!!true!=!false)... // what? besides, your new variables are pointers to alt_u8, not arrays of alt_u8 (it's different). You eventually typecast to void* (so who cares?), but it's still bad form. - Altera_Forum
Honored Contributor
--- Quote Start --- What the other member suggested is generally exact. PIOs are very inefficient if you want to write an external register and aMM slave interface is more convenient. But in your case I see you don't have a 'real' memory interface, being your external module totally asynchronous (and maybe combinatorial?): so I don't see any improvement in switching from PIO to Avalon MM. However giving you informatio about Avalon MM here is not feasible, since it would require a lot of space. You'll learn more by browsing Nios/sopc documentation or searching in the forum. Regards Cris --- Quote End --- Thanks Cris. Yes it is combinatorial with a the module just doing some addition/subtraction on 8 8-bit numbers and giving out an 8-bit result. Concerning the lack of improvement in switching from PIO to MM, I have to say that my application is for learning purposes and so I want to explore all the possibilities. Anyway, from reading literature, I found I could create a custom instruction that would work with combinational circuits. The custom instruction module accepts two 32-bit inputs and give out a 32-bit output, which is more or less what my module needs. So i implemented that. But now I don't know how to pass the inputs to that custom logic module. Given that my inputs are for example 1, 2,.., 8, I thought I could have the data in two arrays and then send these arrays as pointers by calling the builtinn_custom_inpp as:
But I am not getting the right result this way. Is the use of pointers correct in this situation? If yes, can somebody please help me find where I have gone wrong in my usage of pointers? Thanks# define ALT_CI_MYCALC_C_INSTR_N 0x0# define ALT_CI_MYMACRO(A,B) __builtin_custom_inpp ALT_CI_MYCALC_C_INSTR_N (A), (B)) alt_u8 small_1= {1,2,3,4}; alt_u8 small_2= {5,6,7,8}; alt_u8 *array_1 = &small_1; alt_u8 *array_2 = &small_2; int custom_int_res = 0; custom_int_res = ALT_CI_MYMACRO((void *)array_1,(void *)array_2);