cvhdlfpganios

How to transfer two 64 bit from the nios to VHDL using the avalon bus?


First some backstory on this problem. In my current project I'm trying to create a Mandelbrot calculator which is optimized by using a FPGA. At this point I have attempted to establish a bridge between the nios processor and the FPGA (sadly with no luck what so ever).

I'm trying to understand the communication between the nios and the FPGA (which runs VHDL) using the Avalon bus. I've been using VHDL for over 15 weeks now and started the past 5 weeks on the nios 2 processor. Now the thing I want to accomplish is the following: question accomplishment So I want to create a setup on which I can test sending two times a 64 bit value, remember this value in VHDL and them try reading it so it gets back on the Nios 2 processor (in C code).

Of course I did not put this question up here before trying to figure this out myself. This is the work I have done until now.

On the nios I have created a simple setup in the main which writes two 64 bit values to the FPGA, then retrieves them and show the output on the red and green leds, btw: I run this on a Altera DE2 board. The C code looks like this

int main (void)
{
    //Reset the green and red leds to off
    IOWR(REDLEDS_BASE, 0, 0x0);
    IOWR(GREENLEDS_BASE, 0, 0x0);

    //Write the 64 bit x coordinate
    IOWR(MANDELBROT_CORE_BASE, 0x0, 0xAAAAAAAA);
    IOWR(MANDELBROT_CORE_BASE, 0x4, 0xAAAAAAAA);

    //Write the 64 bit y coordinate
    IOWR(MANDELBROT_CORE_BASE, 0x8, 0xEEEEEEEE);
    IOWR(MANDELBROT_CORE_BASE, 0x12, 0xEEEEEEEE);

    //Read the 64 bit x coordinate
    double x = IORD(MANDELBROT_CORE_BASE, 0);

    //Read the 64 bit y coordinate
    double y = IORD(MANDELBROT_CORE_BASE, 8);

    //Write the values to the leds
    IOWR(REDLEDS_BASE, 0, x);
    IOWR(GREENLEDS_BASE, 0, y);

    while(bRunning == true)
    {
    }

    return 1;
}

I'm aware of the fact that this code may be incorrect due the fact that IQRD can only retrieve a 32 bit value. But I am unable to find a solution to reading a 64 bit address all at once. I got most of the techniques on how to do this from this question. So I do not know if this is correct.

Secondly there is the FPGA side written in VHDL. This component is a 64 bit component which is connected in QSYS with the avalon bus of the nios. The component that should handle the incoming and outgoing requests is the avalon_mandelbrot component (given below).

entity avalon_mandelbrot is
    port (
        avs_s0_read        : in  std_logic                     := '0'; -- s0.read
        avs_s0_readdata    : out std_logic_vector(63 downto 0);        -- readdata
        avs_s0_write       : in  std_logic                     := '0'; -- write
        avs_s0_writedata   : in  std_logic_vector(63 downto 0) := (others => '0'); -- writedata
        avs_s0_waitrequest : out std_logic;                                        -- waitrequest
        avs_s0_address     : in  std_logic_vector(7 downto 0)  := (others => '0'); -- address
        avs_s0_byteenable  : in  std_logic_vector(7 downto 0) ;                    -- byte enable
        clk                : in  std_logic                     := '0';             -- clock
        reset              : in  std_logic                     := '0';             -- reset
    );
end entity avalon_mandelbrot;

architecture rtl of avalon_mandelbrot is
   begin
       process(clk)
           variable data_in : std_logic_vector(63 downto 0):= (others => '0');
           variable data_temp_x : std_logic_vector(63 downto 0):= (others => '0');
           variable data_temp_y : std_logic_vector(63 downto 0):= (others => '0');
       begin

           if rising_edge(clk) then

               if avs_s0_write = '1' then
                   if avs_s0_byteenable(0) = '1' then
                       data_in(7 downto 0) := avs_s0_writedata(7 downto 0);
                   end if;

                   if avs_s0_byteenable(1) = '1' then
                       data_in(15 downto 8) := avs_s0_writedata(15 downto 8);
                   end if;

                   if avs_s0_byteenable(2) = '1' then
                       data_in(23 downto 16) := avs_s0_writedata(23 downto 16);
                   end if;

                   if avs_s0_byteenable(3) = '1' then
                       data_in(31 downto 24) := avs_s0_writedata(31 downto 24);
                   end if;

                   if avs_s0_byteenable(4) = '1' then
                       data_in(39 downto 32) := avs_s0_writedata(39 downto 32);
                   end if;

                   if avs_s0_byteenable(5) = '1' then
                       data_in(47 downto 40) := avs_s0_writedata(47 downto 40);
                   end if;

                   if avs_s0_byteenable(6) = '1' then
                       data_in(55 downto 48) := avs_s0_writedata(55 downto 48);
                   end if;

                   if avs_s0_byteenable(7) = '1' then
                       data_in(63 downto 56) := avs_s0_writedata(63 downto 56);
                   end if;
               end if;

        
               --Master wants to write to slave
               if avs_s0_write = '1' then
                   case avs_s0_address is
                       when "00000000" => -- ADDR 0
                           data_temp_x(31 downto 0) := data_in(31 downto 0);
                       when "00000100" => -- ADDR 4
                           data_temp_x(63 downto 32) := data_in(63 downto 32);
                       when "00001000" => -- ADDR 8
                           data_temp_y(31 downto 0) := data_in(31 downto 0);
                       when "00001100" => -- ADDR 12
                           data_temp_y(63 downto 32) := data_in(63 downto 32);
                   end case;
               end if;
        
               --Master wants to read from slave
               if avs_s0_read = '1' then
                   case avs_s0_address is
                       when "00000000" =>
                           avs_s0_readdata <= data_temp_x;
                       when "00001000" =>
                           avs_s0_readdata <= data_temp_y;
                       when others =>
                           avs_s0_readdata <= (others => '0');
                   end case;
               end if;
           end if;
       end process;
end architecture rtl;

It seems logic to me that this setup should work, but when I try to test the whole thing it does not seem to function as it should. Clearly I'm doing something wrong here, maybe that someone with a bit more experience can take a look at it. Hopefully someone will be able to help me with this soon.


Solution

  • I have not worked much with communication with a NIOS specifically, but I have worked with Altera's Avalon bus interface.

    If you have not already done so, I would read their reference material. www.altera.com/literature/manual/mnl_avalon_spec.pdf

    Particularly section 3.5.1 that gives an example of a typical transfer.

    In your example, you did not specify that you are using fixed wait state times for this particular Avalon interface. I'm not sure if that's configurable on the NIOS, but typically fixed wait states is not the default operation of an Avalon bus. Meaning that you need to use the avs_s0_waitrequest signal to signal to the master (NIOS) when a read/write is complete. This port is unconnected in your design.

    In your case, it may be as simple as connecting avs_s0_waitrequest to avs_s0_write during a write operation, and avs_s0_read during a read as your read latency is 1.