Forum Discussion
Questions regarding interfacing of & accessing custom hardware blocks
Hello,
Firstly, before you read the rest - i'm pretty "green" with FPGAs, but i do know how to set up a Nios II soft-core processor system with IP blocks using the SOPC builder, and instantiating it in Quartus II using the top-level hdl files (vhdl). And it works fine with the C code i have written to execute on it. Now I want to implement a certain, often executed, part of the C code as a hardware block ("IP block" i guess), to work as a pheripheral with the Nios II. From what i have gathered i obviously need the VHDL code for the hardware block that i want to create, but i'm in doubt regarding three things (mainly): - Does this VHDL code need to take anything special into account regarding the Avalon switch fabric? i.e. new port/signal names, etc.? I have read http://www.altera.com/literature/hb/qts/qts_qii5v4_01.pdf but i'm not sure if this means i shouldn't think of the Avalon Switch fabric in any way in my HDL. - How do I communicate between my C code executing on the Nios II and the hardware accelerator? By using addresses? Could someone show me an example on how this should be done? Does a tutorial on this exist, covering just a really simple example, like a multiplier as a hardware accelerator, and how this is "interfaced" both regarding calls from a C code and in SOPC builder? Basically what i want to do is what C2H can do for me, but i'd like to learn by doing it manually. I don't care if i can't do it as well as C2H. Thanks for any help.12 Replies
- Altera_Forum
Honored Contributor
--- Quote Start --- Hi again, Ok, this really ought to be easy... i guess this is just a case of RTFM, and do it properly.. and then it would occured to me that there is no "magic" tricks regarding the Avalon - there's a address bus - just what i was looking for. Forget my previous post. Now the VHDL code looks like this process regarding the Avalon interface:
Regardless of what i'm doing, and even adding a case statement for readdata to read some of the values that are written to the variable/signals... But this does not work - no matter what i do (regardless of using IORD/IOWR and IORD_32DIRECT/IOWR_32DIRECT The code i use is:process (clk) begin if clk'event and clk = '1' then if reset_n = '0' then readdata <= (others => '0'); else if chipselect = '1' then -- our HW is selected if read = '1' then -- if the HW wants to write to Nios (avalon should read from HW) if address ="011100" then -- 0x1C (28) readdata <= branch_cost; -- output this new branch cost. else readdata <= "11111111111111111111111111111111"; -- return when nothing has happened. end if; end if; elsif write = '1' then -- if write is asserted (avalon should write to the HW) case address is when "000000" => -- 0x00 sys_rec <= writedata; when "000100" => -- 0x04 (4) sys_ref <= writedata; when "001000" => -- 0x08 (8) par1rec <= writedata; when "001100" => -- 0x0C (12) par1ref <= writedata; when "010000" => -- 0x10 (16) par2rec <= writedata; when "010100" => -- 0x14 (20) par2ref <= writedata; when "011000" => --0x18 (24) x <= writedata; when others => null; end case; end if; end if; end if; end process;
I have tried defining sysref as 4 and 0x04, but by the same results No matter what i have done, it always prints that: sysrech=0 and sysrecd=0 My entity of the VHDL is just:# include "system.h"# include <io.h> . . .# define WR_test(offset,data) IOWR_32DIRECT(BCC_TEST_BASE,offset,data)# define RD_test(offset) IORD_32DIRECT(BCC_TEST_BASE,offset)# define sysref 4 int main(void) { WR_test(sysref,1); WR_test(par1ref,3); printf("sysrech=%x", RD_test(sysref)); printf("sysrecd=%d", RD_test(sysref)); return 0; }
Any hints? From importing the component in SOPC builder, there were absolutely no errors, and the write and read waveforms also looked as expected. Thanks for any hints regarding why it always throws a zero back... :) --- Quote End --- did find the answer?entity bcc_test port ( signal clk, reset_n, read, write, chipselect : in std_logic; signal readdata : out std_logic_vector(31 downto 0); signal writedata : in std_logic_vector(31 downto 0); signal address : in std_logic_vector(5 downto 0) ); end bcc_test; - Altera_Forum
Honored Contributor
I have the same problem no matter what i do.
Did you succeed on reading and writing to your component? - Altera_Forum
Honored Contributor
Ah that is interesting. The spec has changed. My assumption is that they are trying to get people to move away from using chipselect on regular slave interfaces.
However, I have to assume that the interface still behaves to it's original spec. If the component editor allows you to add chipselect as a slave signal, then the fabric knows what to do with it. I normally do not use the chipselect signal as it's provided with "read" and "write". The only time I use it is when I'm actually interfacing to an external IC that needs the signal (even then you could derive it from read and write). Jake - Altera_Forum
Honored Contributor
I had a look at the avalon interface specifications (http://www.altera.com/literature/manual/mnl_avalon_spec.pdf?gsa_pos=5&wt.oss_r=1&wt.oss=avalon) before posting, and if I'm not mistaken the chipselect signal only exists on tri-stated Avalon MM interfaces.
- Altera_Forum
Honored Contributor
Chipselect does exist in Avalon but it is optional. If you use chipselect, you must gate read and write with it. If you don't use chipselect, read and write are only asserted when your component is selected.
- Altera_Forum
Honored Contributor
Where does your chipselect signal come from? AFAIK it doesn't exist in the Avalon interface. Just check read and write inputs, they are only asserted when your component is addressed.
- Altera_Forum
Honored Contributor
Hi again,
Ok, this really ought to be easy... i guess this is just a case of RTFM, and do it properly.. and then it would occured to me that there is no "magic" tricks regarding the Avalon - there's a address bus - just what i was looking for. Forget my previous post. Now the VHDL code looks like this process regarding the Avalon interface:
Regardless of what i'm doing, and even adding a case statement for readdata to read some of the values that are written to the variable/signals... But this does not work - no matter what i do (regardless of using IORD/IOWR and IORD_32DIRECT/IOWR_32DIRECT The code i use is:process (clk) begin if clk'event and clk = '1' then if reset_n = '0' then readdata <= (others => '0'); else if chipselect = '1' then -- our HW is selected if read = '1' then -- if the HW wants to write to Nios (avalon should read from HW) if address ="011100" then -- 0x1C (28) readdata <= branch_cost; -- output this new branch cost. else readdata <= "11111111111111111111111111111111"; -- return when nothing has happened. end if; end if; elsif write = '1' then -- if write is asserted (avalon should write to the HW) case address is when "000000" => -- 0x00 sys_rec <= writedata; when "000100" => -- 0x04 (4) sys_ref <= writedata; when "001000" => -- 0x08 (8) par1rec <= writedata; when "001100" => -- 0x0C (12) par1ref <= writedata; when "010000" => -- 0x10 (16) par2rec <= writedata; when "010100" => -- 0x14 (20) par2ref <= writedata; when "011000" => --0x18 (24) x <= writedata; when others => null; end case; end if; end if; end if; end process;
I have tried defining sysref as 4 and 0x04, but by the same results No matter what i have done, it always prints that: sysrech=0 and sysrecd=0 My entity of the VHDL is just:# include "system.h"# include <io.h> . . .# define WR_test(offset,data) IOWR_32DIRECT(BCC_TEST_BASE,offset,data)# define RD_test(offset) IORD_32DIRECT(BCC_TEST_BASE,offset)# define sysref 4 int main(void) { WR_test(sysref,1); WR_test(par1ref,3); printf("sysrech=%x", RD_test(sysref)); printf("sysrecd=%d", RD_test(sysref)); return 0; }
Any hints? From importing the component in SOPC builder, there were absolutely no errors, and the write and read waveforms also looked as expected. Thanks for any hints regarding why it always throws a zero back... :)entity bcc_test port ( signal clk, reset_n, read, write, chipselect : in std_logic; signal readdata : out std_logic_vector(31 downto 0); signal writedata : in std_logic_vector(31 downto 0); signal address : in std_logic_vector(5 downto 0) ); end bcc_test; - Altera_Forum
Honored Contributor
Hi again,
Ok, the VHDL code has been completed, and decided to use the proposed form of interfacing with avalon as above, but some modifications, since i want to write to multiple input signals:
With the following signals in the port declaration in the entity:begin process (clk) begin if clk'event and clk = '1' then if reset_n = '0' then readdata <= (others => '0'); else if chipselect = '1' then -- our BCC is selected if read = '1' then -- if the hardware wants to write to avalon (avalon should read from hardware) if flag_bc = '1' then -- if a branch_cost is done readdata <= "11111111111111111111111111111111"; readdata <= result; elsif flag_bc = '0' then -- if branch cost is not done, just send 32 zeros readdata <= "00000000000000000000000000000000"; end if; end if; elsif write = '1' then -- if write is asserted (avalon should write to the hardware) case count is -- The order in which inputs are fed from C is fixed when "000" => sys_rec <= writedata; count <= "001"; when "001" => sys_ref <= writedata; count <= "010"; when "010" => par1rec <= writedata; count <= "011"; when "011" => par1ref <= writedata; count <= "100"; when "100" => par2rec <= writedata; count <= "101"; when "101" => par2ref <= writedata; count <= "110"; when "110" => x <= writedata; count <= "000"; end case; end if; end if; end if; end process;
It seems to import in SOPC builder, with a warning: avalon_slave_0: signal readdata appears 8 times (only once is allowed). Also, the case conditional statement has been made so that i write from the C program, in a specific order, i.e. when it has received the first 32 bits, it assigns them to sys_rec, the next 32 bits are assigned to sys_ref and so on. flag_bc is a flag which is set in another process as soon as result is computed. When the flag is 0 i want the hardware accelerator to return 32 bits of zeros. When the flag is 1 i want it to send 32 bits of ones (to indicate that there is a result) and then afterwards give me the (32 bit) result. Would this actually work as it is supposed? I suppose i am reading from the register writedata, and sending to the register readdata. How do i know in which order they come? For example would readdata be at IORD(0x01001000,0) or would it be IORD(0x01001000,1) or something else... If the rest of the vhdl can help anything, i can post that also, but it needs to be shined up a bit.signal clk, reset_n, read, write, chipselect : in std_logic; signal readdata : out std_logic_vector(31 downto 0); signal writedata : in std_logic_vector(31 downto 0) ); - Altera_Forum
Honored Contributor
Hmm, i'm not sure i understand - how would i know whether i have register addresses or hardware addresses? Does this depend on how i interface to the avalon?
I really feel stupid regarding this. What i want to do is send multiple 32 bit integers from the C code to the hardware block, to different port inputs, and then also be able to get multiple outputs, which are also 32 bit, by reading the output pins. I know the macros to write and read, and the base address of the hardware block, but i don't know what the "address increment" or second input to the macros should be. I took a look at http://www1.cs.columbia.edu/~sedwards/classes/2007/4840/lab3.pdf figure 11 and figure 16, but i'm still not sure how writing to it works completely. What i'm not sure of right now, is also why only a clock is required for slave avalon-mm blocks as a minimum. Since I would also like to read outputs from and write inputs to it, so i would have to use write, writedata and read, readdata, and a chipselect i guess - i wouldn't expect SOPC Builder to figure it out otherwise. I'm thinking that having a separate process doing the following (not checked it for errors, and probably still missing some) would work:
In other words, very much like the tutorial linked to above.process (clk) begin if clk'event and clk ='1' then if chipselect ='1' then if read='1' then readdata <= take_an_input; elsif write='1' then writedata <= output_eg_result_from_another_process; end if; end if; end if; end process; - Altera_Forum
Honored Contributor
If the addresses of the hardware component are register addresses and not memory addresses, then you really ought to use the IOWR macro.
An example. Suppose you've got some hardware component in your system named "my_custom_hw" and you want to write values to registers 0 and 1 within your component:
The other macros (IOWR_8DIRECT, IOWR_16DIRECT, IOWR_32DIRECT) are really for writing to memory addressed components. If you want to see the macros, just look at: C:\altera\90\nios2eds\components\altera_nios2\HAL\inc\io.h The "MY_CUSTOM_HW_BASE" macro is generated in the "system.h" file. So you never need to know what the base addresses of your components are, you just use the macro. Jake# include <io.h> // Contains IOWR and IORD macros # include "system.h" // Includes everything you need to know about your system. ... void some_function(){ ... IOWR(MY_CUSTOM_HW_BASE, 0, 0x12345678); // Write to register offset 0 of "my_custom_hw" IOWR(MY_CUSTOM_HW_BASE, 1, 0x87654321); // Write to register offset 1 of "my_custom_hw" }