----------------------------------------------------------------------------------
--
-- Copyright CERN 2011.
-- 
-- This documentation describes Open Hardware and is licensed under the
-- CERN OHL v. 1.1.
--
-- You may redistribute and modify this documentation under the terms of the CERN
-- OHL v.1.1. (http://ohwr.org/cernohl).
--
-- This documentation is distributed WITHOUT
-- ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING OF MERCHANTABILITY, SATISFACTORY
-- QUALITY AND FITNESS FOR A PARTICULAR PURPOSE. Please see the CERN OHL v.1.1 for
-- applicable conditions.
--
----------------------------------------------------------------------------------
--
-- Company:        CERN, BE-CO-HT 
-- Engineer:       Carlos Gil Soriano
-- 
-- Create Date:    23:55:10 06/10/2012 
-- Design Name:    m25p32 wishbone access
-- Module Name:    m25p32_regs - Behavioral 
-- Project Name:   Level Conversion Circuits
-- Target Devices: Spartan 6
-- Tool versions: 
-- Description:    This is a wishbone slave able to access m25p32 Non volatile
--                 Flash devices and manage the write and read to the memory.
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
library work;
use IEEE.STD_LOGIC_1164.ALL;
--! Insert this line for conversion functions
use IEEE.NUMERIC_STD.ALL;
use work.m25p32_pkg.ALL;
use work.spi_master_pkg.ALL;

entity m25p32_regs is
   generic(
           g_INST_LENGTH    : NATURAL := c_INST_LENGTH;  
           g_ADDR_LENGTH    : NATURAL := c_ADDR_LENGTH; 
           g_DATA_LENGTH    : NATURAL := c_DATA_LENGTH;
           g_READ_LENGTH    : NATURAL := c_READ_LENGTH;
           g_WB_ADDR_LENGTH : NATURAL := c_BYTES_PER_PAGE_BITS + 1);
   port (
           wb_rst_i      : in  STD_LOGIC; 
           wb_clk        : in  STD_LOGIC; 

           wb_we_i       : in  STD_LOGIC;  
           wb_stb_i      : in  STD_LOGIC;
           wb_cyc_i      : in  STD_LOGIC;
           wb_sel_i      : in  STD_LOGIC_VECTOR (3  downto 0);
           wb_data_i     : in  STD_LOGIC_VECTOR (31 downto 0); 
           wb_data_o     : out STD_LOGIC_VECTOR (31 downto 0); 
           wb_addr_i     : in  STD_LOGIC_VECTOR (g_WB_ADDR_LENGTH downto 0); 
           wb_ack_o      : out STD_LOGIC;
           wb_rty_o      : out STD_LOGIC;
           wb_err_o      : out STD_LOGIC;

           wr_data_o     : out STD_LOGIC_VECTOR(g_DATA_LENGTH*8 - 1 downto 0);
           rd_data_i     : in  STD_LOGIC_VECTOR(g_READ_LENGTH*8 - 1 downto 0);
           SR_m25p32_o   : out STD_LOGIC_VECTOR(r_SR_m25p32'a_length - 1 downto 0);

           FMI_io        : inout STD_LOGIC_VECTOR (r_FMI'a_length - 1 downto 0)
        );
end m25p32_regs;

architecture Behavioral of m25p32_regs is

   signal s_FMI        : r_FMI;
   signal s_SR_m25p32  : r_SR_m25p32;

   signal s_wr_page    : r_page;
   signal s_rd_word    : r_word;

   signal s_wb_ack_o   : STD_LOGIC;
   signal s_wb_rty_o   : STD_LOGIC;
   signal s_wb_err_o   : STD_LOGIC;

begin

   wb_ack_o         <= s_wb_ack_o;
   wb_rty_o         <= s_wb_rty_o;
   wb_err_o         <= s_wb_err_o;

   wr_data_o        <= f_STD_LOGIC_VECTOR(s_wr_page);

   FMI_io            <= f_STD_LOGIC_VECTOR(s_FMI);

   p_wbslave:   process (wb_clk)
      variable v_FMI : r_FMI;
      variable v_pos : NATURAL;
   begin
      if rising_edge(wb_clk) then
         if wb_rst_i = '1' then
            s_FMI       <=  c_FMI_default;

            wb_data_o    <= (others => '0');
            s_wb_ack_o   <= '0'; 
            s_wb_rty_o   <= '0'; 
            s_wb_err_o   <= '0'; 

         else
            --! We never retry
            s_wb_rty_o  <= '0';

            if (wb_stb_i = '1' and wb_cyc_i = '1') then
               if (s_wb_ack_o or s_wb_rty_o or s_wb_err_o) = '1' then
                  s_wb_ack_o  <= '0'; 
                  s_wb_err_o  <= '0'; 
               else
                  s_wb_ack_o  <= '1';
                  s_wb_err_o  <= '0';
                  if wb_we_i = '1' then
                     case wb_addr_i(g_WB_ADDR_LENGTH) is
                        when '0' =>
                           case wb_addr_i(1 downto 0) is
                              --! FMI address
                              when "00"   =>
                                 if s_FMI.OPA = '0' then
                                    v_FMI       := f_FMI(wb_data_i(23 downto 0)); 
                                    s_FMI       <= v_FMI;
                                 else
                                    s_wb_ack_o  <= '0';
                                    s_wb_err_o  <= '1';
                                 end if;
                              --! SR address
                              when "01"  =>
                                 s_SR_m25p32 <= f_SR_m25p32(wb_data_i(
                                                r_SR_m25p32'a_length-1 downto 0));
                              --! READ_DATA address 
                              when others =>
                                 s_wb_ack_o  <= '0';
                                 s_wb_err_o  <= '1';
                           end case;
                        --! WRITE_DATA address pool
                        when others    =>
                           v_pos := to_integer(UNSIGNED(wb_addr_i(g_WB_ADDR_LENGTH
                                         - 1 downto 0)));
                           s_wr_page(v_pos).word_slv <= wb_data_i;
                     end case;
                  else
                     case wb_addr_i(g_WB_ADDR_LENGTH) is
                        when '0'    =>
                           case wb_addr_i(1 downto 0) is
                              when "00"   =>
                                 wb_data_o(r_FMI'a_length - 1 downto 0) <=  FMI_io;
                                 wb_data_o(31 downto r_FMI'a_length)
                                                                <= (others => '0');
                              when "01"   =>
                                 wb_data_o(r_SR_m25p32'a_length - 1 downto 0)
                                                <= f_STD_LOGIC_VECTOR(s_SR_m25p32);
                                 wb_data_o(31 downto r_SR_m25p32'a_length)
                                                                <= (others => '0');
                              when "10"   => 
                                 wb_data_o <= f_STD_LOGIC_VECTOR(s_rd_word);
                              when others =>
                                 s_wb_ack_o  <= '0';
                                 s_wb_err_o  <= '1';
                           end case;
                        when others =>
                           wb_data_o <= f_STD_LOGIC_VECTOR(
                             s_wr_page(to_integer(
                             UNSIGNED(wb_addr_i(g_WB_ADDR_LENGTH - 1 downto
                             0)))));
                     end case;
                  end if;
               end if;
            end if;
         end if;
      end if;
   end process;

end Behavioral;
