-- Copyright(c) 2022 UNITED KINGDOM RESEARCH AND INNOVATION
-- Electronic System Design Group, Technology Department,
-- Science and Technology Facilities Council
-- Licensed under the BSD 3-Clause license. See LICENSE file in the project root for details.
library ieee;
use ieee.std_logic_1164.all;

library axi4_lib;
use axi4_lib.axi4lite_pkg.all;

entity axi4lite_slave_logic is
    port(
        axi4lite_aclk    : in  std_logic;
        axi4lite_aresetn : in  std_logic;
        axi4lite_mosi    : in  t_axi4lite_mosi;
        axi4lite_miso    : out t_axi4lite_miso;
        ipb_mosi         : out t_ipb_mosi;
        ipb_miso         : in  t_ipb_miso
    );
end entity axi4lite_slave_logic;

architecture axi4lite_slave_logic_a of axi4lite_slave_logic is

    -- Types: ------------------------------------------------------------------
    type t_states is (
        idle,
        reading_writing,
        waiting
    );

    --
    -- Internal signals: -------------------------------------------------------
    signal state        : t_states;
    signal ipb_mosi_int : t_ipb_mosi;
    signal writing      : std_logic := '0';
    signal axi_valid    : std_logic := '0';
    signal axi_ready    : std_logic;
    signal ipb_ack      : std_logic;
    signal ipb_req      : std_logic := '0';

begin

    ipb_mosi_int.wdat <= axi4lite_mosi.wdata;
    ipb_mosi          <= ipb_mosi_int;

    axi4lite_miso.rresp <= c_axi4lite_resp_okay;
    axi4lite_miso.bresp <= c_axi4lite_resp_okay;
    main_proc : process(axi4lite_aclk, axi4lite_aresetn)
    begin
        if axi4lite_aresetn = '0' then
            axi4lite_miso.arready <= '0';
            axi4lite_miso.awready <= '0';
            axi_valid             <= '0';
            ipb_req               <= '0';
            axi4lite_miso.wready  <= '0';
            state                 <= idle;
            writing               <= '0';
            axi4lite_miso.rdata   <= (others => '0');

        elsif rising_edge(axi4lite_aclk) then
            case state is
                when idle =>
                    axi4lite_miso.arready <= '1';
                    axi4lite_miso.awready <= '1';
                    if axi4lite_mosi.arvalid = '1' then --address axi transaction read
                        axi4lite_miso.arready <= '0';
                        axi4lite_miso.awready <= '0'; --dont accept any more requests
                        writing               <= '0'; --set to whether you are reading/writing
                        ipb_req               <= '1'; --request ipd transaction
                        ipb_mosi_int.addr     <= axi4lite_mosi.araddr;
                        state                 <= reading_writing;
                    elsif axi4lite_mosi.awvalid = '1' then --address axi transaction write
                        axi4lite_miso.arready <= '0';
                        axi4lite_miso.awready <= '0'; --dont accept any more requests
                        writing               <= '1'; --set to whether you are reading/writing
                        ipb_mosi_int.addr     <= axi4lite_mosi.awaddr;
                        state                 <= reading_writing;
                    end if;
                when reading_writing =>
                    if ipb_ack = '1' and ipb_req = '1' then --on idp transaction
                        ipb_req              <= '0';
                        axi_valid            <= '1'; --say axi output stream is valid
                        axi4lite_miso.wready <= writing; --if writing ready to write data
                        state                <= waiting;
                        if (writing = '0') then
                            axi4lite_miso.rdata <= ipb_miso.rdat;
                        end if;
                    else
                        if writing = '1' then
                            ipb_req <= axi4lite_mosi.wvalid;
                        end if;
                    end if;
                when waiting =>
                    axi4lite_miso.rdata <= (others => '0');
                    if axi_ready = '1' then --on axi transation
                        axi_valid             <= '0'; --say axi output is not valid
                        axi4lite_miso.wready  <= '0'; --not ready to write data
                        axi4lite_miso.arready <= '1'; --ready to accept new transactions
                        axi4lite_miso.awready <= '1';
                        state                 <= idle;
                    end if;
            end case;
        end if;
    end process main_proc;

    comb_proc : process(writing, ipb_miso, axi_valid, axi4lite_mosi, ipb_req)
    begin
        if writing = '0' then           --if reading
            axi_ready            <= axi4lite_mosi.rready;
            axi4lite_miso.rvalid <= axi_valid;
            axi4lite_miso.bvalid <= '0';
            ipb_mosi_int.rreq    <= ipb_req;
            ipb_mosi_int.wreq    <= '0';
            ipb_ack              <= ipb_miso.rack;
        else                            --if writing
            axi_ready            <= axi4lite_mosi.bready;
            axi4lite_miso.bvalid <= axi_valid;
            axi4lite_miso.rvalid <= '0';
            ipb_mosi_int.rreq    <= '0';
            ipb_mosi_int.wreq    <= ipb_req;
            ipb_ack              <= ipb_miso.wack;
        end if;
    end process comb_proc;

end architecture axi4lite_slave_logic_a;
