----------------------------------------------------------------------------------
--
-- 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:    12:00:00 15-06-2012 
-- Design Name:
-- Module Name:    m25p32_pkg
-- Project Name:   Level Conversion Circuits 
-- Target Devices: Spartan 6
-- Tool versions:  Xilinx ISE 13.4
-- Description:    This package holds the definition of the register used along
--                 the m25p32 core. It acts the logic that glues together the
--                 following modules:
--                   - m25p32_core.vhd
--                   - m25p32_regs.vhd
--                     - m25p32_buff.vhd
--                 Apart from this package another one -m25p32_core_pkg.vhd-
--                 has been written. This second package holds the procedures
--                 that govern the fsm in m25p32_core.vhd.
--                 By splitting the packages in two, the circuit is easier to
--                 in m25p32_core.vhd and this package is less heavy.
--
-- Dependencies:   spi_master_pkg.vhd (SPI master multifield) 
--
-- Revision: 0.1
--   0.01          + File Created
--                 
-- Additional Comments: enjoy it! ;) 
--
----------------------------------------------------------------------------------




library IEEE;
library work;
use IEEE.STD_LOGIC_1164.all;
use IEEE.NUMERIC_STD.ALL;
use work.spi_master_pkg.ALL;

package m25p32_pkg is

   --! Constant only used in simulation
   constant c_WISHBONE_PERIOD   : TIME      := 50 ns;  --! Working at 20MHz 

   --! General constants
   constant c_NUMBER_OF_BUFFERS : NATURAL   := 2;
   constant c_PAGE_SIZE         : NATURAL   := 256;   --! Bytes in a page
   constant c_CPOL              : STD_LOGIC := '0';
   constant c_CPHA              : STD_LOGIC := '0';

   --! Mapping of the registers
   constant c_CTR0_addr         : STD_LOGIC_VECTOR (3  downto 0) := X"0"; 
   constant c_CTR1_addr         : STD_LOGIC_VECTOR (3  downto 0) := X"1"; 
   constant c_BUF0_addr         : STD_LOGIC_VECTOR (3  downto 0) := X"2"; 
   constant c_BUF1_addr         : STD_LOGIC_VECTOR (3  downto 0) := X"3"; 
   constant c_FMOH_addr         : STD_LOGIC_VECTOR (3  downto 0) := X"4";
   constant c_DATA_BUF_addr     : STD_LOGIC_VECTOR (3  downto 0) := X"5";
   constant c_SR_m25p32_addr    : STD_LOGIC_VECTOR (3  downto 0) := X"F";


   --! Attribute length for every register
   attribute a_length : NATURAL;

   ----------------------------------------
   -- ConTRol 0 Register
   ----------------------------------------
   -- 8 bits
   -- Wishbone access: Read-only 
   ----------------------------------------
   --  BIT        NAME         DESCRIPTION
   --   0          OPA          OPeration Attended 
   --   1          OPF          OPeration Finished 
   --   7-2        x            Reserved   
   ----------------------------------------
   type r_CTR0 is
      record
         OPA        : STD_LOGIC;
         OPF        : STD_LOGIC;
         x          : STD_LOGIC_VECTOR(7  downto 2);
      end record;

   attribute a_length of r_CTR0      : type is 8;


   ----------------------------------------
   -- ConTRol 1 Register
   ----------------------------------------
   -- 16 bits
   -- Wishbone access: Read-only 
   ----------------------------------------
   --  BIT           NAME      DESCRIPTION
   --    [A-1] - 0    AWBF      Active Write Buffer Flag
   --   [2A-1] - A    ARBF      Active Read Buffer Flag
   --   [3A-1] -2A    CBSF      Clear Buffer Status Flag
   --      6          PUSH_I    PUSH contents into Active Instruction Buffer 
   --      7          PUSH_A    PUSH contents into Active Address Buffer 
   --      8          PUSH_D    PUSH contents into Active Data Buffer
   --      9          BOV       Buffer OVerride
   --     10          FS        Force buffers Shift
   --  18-11          CNTA      internal CouNTer of the Active buffer
   --  23-19          x         Reserved
   ----------------------------------------
   --  A   is  c_NUMBER_OF_BUFFERS
   ----------------------------------------
   type r_CTR1 is
      record
         AWBF      : STD_LOGIC_VECTOR (  c_NUMBER_OF_BUFFERS - 1 downto 0);
         ARBF      : STD_LOGIC_VECTOR (2*c_NUMBER_OF_BUFFERS - 1
                                             downto   c_NUMBER_OF_BUFFERS);
         CBSF      : STD_LOGIC_VECTOR (3*c_NUMBER_OF_BUFFERS - 1
                                             downto 2*c_NUMBER_OF_BUFFERS);
         PUSH_I    : STD_LOGIC;
         PUSH_A    : STD_LOGIC;
         PUSH_D    : STD_LOGIC;
         BOV       : STD_LOGIC;
         FS        : STD_LOGIC;
         CNTA      : UNSIGNED(18 downto 11);
         x         : STD_LOGIC_VECTOR(23 downto 19);
      end record;

   attribute a_length of r_CTR1      : type is 24; 


   ----------------------------------------
   -- BUFfers m25p32 pointer Register
   ----------------------------------------
   -- 24 bits
   -- Wishbone access: Write-read
   ----------------------------------------
   --  BIT        NAME         DESCRIPTION
   --   7-0        PAGE         PAGE
   --  13-8        SECT         SECTor
   -- 15-14        y            Reserved
   ----------------------------------------
   type r_BUF is
      record
         PAGE      : UNSIGNED (7  downto 0);
         SECT      : UNSIGNED (13 downto 8);
         y         : STD_LOGIC_VECTOR(15 downto 14);
      end record;

   attribute a_length of r_BUF       : type is 16;


   type r_DBUF  is array (0 to 1) of r_BUF;

   attribute a_length of r_DBUF      : type is 48;

   ----------------------------------------                                               
   --  Flash Memory Operation Handler Register                                            
   ----------------------------------------                                               
   --  32 bits
   --  Wishbone access: Write-read                                                        
   ----------------------------------------                                               
   --  BIT        NAME      DESCRIPTION 
   --   2-0        OP        OPeration
   --   3          OPP       OPeration Pending
   --   4          CLBC      CLear Buffer Count
   --   5          FS        Force buffer Shift
   --   14-6       y         Reserved
   --   22-15      PG        PaGe number
   --   28-23      SCT       SeCTor number
   --   31-29      z         Reserved                                                  
   ----------------------------------------
   --  List of operations:
   --  0b000        x            Reserved  
   --  0b001        ERS           ERase sector. Sets everything to FF
   --  0b010        RDP           ReaD Page. Not implemented by now.
   --  0b011        WRP           WRite Page
   --  0b100        RDSR          ReaD Status Register 
   --  0b101        WRSR          WRite Status Register
   ----------------------------------------
   type t_operations is (x, ERS, RDP, WRP, RDSR, WRSR); 

   type r_FMOH is
      record
         OP    : t_operations;
         OPP   : STD_LOGIC;
         CLBC  : STD_LOGIC;
         FS    : STD_LOGIC;
         y     : STD_LOGIC_VECTOR(14 downto  6);
         PG    : STD_LOGIC_VECTOR(22 downto 15);
         SCT   : STD_LOGIC_VECTOR(28 downto 23);
         z     : STD_LOGIC_VECTOR(31 downto 29);
      end record;

   attribute a_length of r_FMOH      : type is 32;


   ----------------------------------------
   -- Status Register in m25p32
   ----------------------------------------
   -- 8 bits
   -- Wishbone access: Write-read
   ----------------------------------------
   --  BIT        NAME      DESCRIPTION
   --    0         WIP       Write In Progress 
   --    1         WEL       Write Enable Latch
   --    2         BP0       Block Protect bit 0 
   --    3         BP1       Block Protect bit 1
   --    4         BP2       Block Protect bit 2
   --  6-5         x         Reserved
   --    7         SRWD      Status Register Write Disabled
   ----------------------------------------
   type r_SR_m25p32 is
      record
         WIP  : STD_LOGIC;
         WEL  : STD_LOGIC;
         BP0  : STD_LOGIC;
         BP1  : STD_LOGIC;
         BP2  : STD_LOGIC;
         x    : STD_LOGIC_VECTOR(6  downto 5);
         SRWD : STD_LOGIC; 
      end record;

   attribute a_length of r_SR_m25p32 : type is 8;


   ---------------------------------------------------------------------------
   --! Default values for the registers
   ---------------------------------------------------------------------------

   --! Default: CTR0 has no operations
   constant c_CTR0_default  : r_CTR0 := (OPA     => '0',
                                         OPF     => '0',
                                         x       => (others => '0'));

   --! Default: CTR1 no buffer is active and count is reset to 0.
   constant c_CTR1_default  : r_CTR1 := (AWBF    => std_logic_vector(
                                          to_unsigned(1,c_NUMBER_OF_BUFFERS)),
                                         ARBF    => std_logic_vector(
                                          to_unsigned(1,c_NUMBER_OF_BUFFERS)),

                                         CBSF    => (others => '0'),
                                         PUSH_I  => '0', 
                                         PUSH_A  => '0', 
                                         PUSH_D  => '0', 
                                         BOV     => '0',
                                         FS      => '0', 
                                         CNTA    => to_unsigned(0, 8),
                                         x       => (others => '0') 
                                      );

   --! Default: FMOH no operation set by default
   constant c_FMOH_default  : r_FMOH := (OP      =>  t_operations'val(0),
                                         OPP     => '0',
                                         CLBC    => '0', 
                                         FS      => '0', 
                                         y       => (others => '0'),
                                         PG      => (others => '0'),
                                         SCT     => (others => '0'),
                                         z       => (others => '0'));

   --! Default: BUF default page and sector is 0,0
   constant c_BUF_default   : r_BUF  := (PAGE    => (others => '0'),
                                         SECT    => (others => '0'),
                                         y       => (others => '0'));

   --! Default: SR_m25p32 no protection in the memory by default
   constant c_SR_m25p32_default : r_SR_m25p32 := (WIP  => '0', 
                                                  WEL  => '0', 
                                                  BP0  => '0', 
                                                  BP1  => '0', 
                                                  BP2  => '0', 
                                                  x    => (others => '0'), 
                                                  SRWD => '0');


   ---------------------------------------------------------------------------
   --! Translation functions back and forth STD_LOGIC_VECTOR
   ---------------------------------------------------------------------------
   function f_STD_LOGIC_VECTOR  (r_register : r_CTR0) return STD_LOGIC_VECTOR;
   function f_STD_LOGIC_VECTOR  (r_register : r_CTR1) return STD_LOGIC_VECTOR;
   function f_STD_LOGIC_VECTOR  (r_register : r_BUF)  return STD_LOGIC_VECTOR;
   function f_STD_LOGIC_VECTOR  (r_register : r_FMOH) return STD_LOGIC_VECTOR;
   function f_STD_LOGIC_VECTOR  (r_register : r_SR_m25p32)
                                                      return STD_LOGIC_VECTOR;

   function f_CTR0      (signal r_register : in STD_LOGIC_VECTOR(7  downto 0))
                                                                return r_CTR0;
   function f_CTR1      (signal r_register : in STD_LOGIC_VECTOR(23 downto 0))
                                                                return r_CTR1;
   function f_BUF       (signal r_register : in STD_LOGIC_VECTOR(15 downto 0))
                                                                return r_BUF;
   function f_FMOH      (signal r_register : in STD_LOGIC_VECTOR(31 downto 0))
                                                                return r_FMOH;
   function f_SR_m25p32 (signal r_register : in STD_LOGIC_VECTOR(7  downto 0))
                                                           return r_SR_m25p32;

   function f_SHIFT_BUFFERS   (       r_register : in r_CTR1;
                                      shift      : in STD_LOGIC) return r_CTR1;
   function f_ACTIVE_BUFFERS  (signal r_register : in r_CTR1) return r_CTR1;


   --! Declaration of m25p32_buff to save space
   component m25p32_buff is
   generic(
       g_BUFFER_SIZE : NATURAL := c_NUMBER_OF_BUFFERS;
       g_inst_width  : NATURAL := c_INST_LENGTH;
       g_addr_width  : NATURAL := c_ADDR_LENGTH;
       g_data_width  : NATURAL := c_PAGE_SIZE
   ); port(
       rst_i         : in  STD_LOGIC;
       clk           : in  STD_LOGIC;

       inst_i        : in  STD_LOGIC_VECTOR (8*g_inst_width - 1 downto 0);
       addr_i        : in  STD_LOGIC_VECTOR (8*g_addr_width - 1 downto 0);
       data_i        : in  STD_LOGIC_VECTOR (31 downto 0);

       inst_db_o     : out STD_LOGIC_VECTOR (g_BUFFER_SIZE*8*g_inst_width - 1
                                                                     downto 0);
       addr_db_o     : out STD_LOGIC_VECTOR (g_BUFFER_SIZE*8*g_addr_width - 1
                                                                     downto 0);
       data_db_o     : out STD_LOGIC_VECTOR (g_BUFFER_SIZE*8*g_data_width - 1
                                                                     downto 0);
       CTR1_i        : in  STD_LOGIC_VECTOR (r_CTR1'a_length - 1 downto 0);
       DBUF_o        : out STD_LOGIC_VECTOR (r_DBUF'a_length - 1 downto 0)
    );
   end component;

end m25p32_pkg;

package body m25p32_pkg is

   -----------------------------------------------------------------------------
   --! @brief CTR0 record type translation to STD_LOGIC_VECTOR
   --! @param r_register r_CTR0 record type to be translated
   -----------------------------------------------------------------------------
   function f_STD_LOGIC_VECTOR  (r_register : r_CTR0) return STD_LOGIC_VECTOR is
   begin
      return (  r_register.x
              & r_register.OPF
              & r_register.OPA); 
   end f_STD_LOGIC_VECTOR;

   -----------------------------------------------------------------------------
   --! @brief CTR1 record type translation to STD_LOGIC_VECTOR
   --! @param r_register r_CTR1 record type to be translated
   -----------------------------------------------------------------------------
   function f_STD_LOGIC_VECTOR  (r_register : r_CTR1) return STD_LOGIC_VECTOR is
   begin
      return ( r_register.x                      &
               std_logic_vector(r_register.CNTA) &
               r_register.FS                     &
               r_register.BOV                    &
               r_register.PUSH_D                 &
               r_register.PUSH_A                 &
               r_register.PUSH_I                 &
               r_register.CBSF                   &
               r_register.ARBF                   &
               r_register.AWBF);
   end f_STD_LOGIC_VECTOR;

   -----------------------------------------------------------------------------
   --! @brief BUF record type translation to STD_LOGIC_VECTOR
   --! @param r_register r_BUF record type to be translated
   -----------------------------------------------------------------------------
   function f_STD_LOGIC_VECTOR  (r_register : r_BUF)  return STD_LOGIC_VECTOR is
   begin
      return ( r_register.y                        &
               std_logic_vector(r_register.SECT)   &
               std_logic_vector(r_register.PAGE));
   end f_STD_LOGIC_VECTOR;

   -----------------------------------------------------------------------------
   --! @brief FMOH record type translation to STD_LOGIC_VECTOR
   --! @param r_register r_FMOH record type to be translated
   -----------------------------------------------------------------------------
   function f_STD_LOGIC_VECTOR  (r_register : r_FMOH) return STD_LOGIC_VECTOR is
   begin
      return (r_register.z    & r_register.SCT  &
              r_register.PG   & r_register.y    &
              r_register.FS   & r_register.CLBC &
              r_register.OPP  & 
              std_logic_vector(to_unsigned(t_operations'pos(r_register.OP),3)) );
   end f_STD_LOGIC_VECTOR; 

   -----------------------------------------------------------------------------
   --! @brief SR_m25p32 record type translation to STD_LOGIC_VECTOR
   --! @param r_register r_SR_m25p32 record type to be translated
   -----------------------------------------------------------------------------
   function f_STD_LOGIC_VECTOR  (r_register : r_SR_m25p32)
                                                      return STD_LOGIC_VECTOR is
   begin
      return ( r_register.SRWD & std_logic_vector(r_register.x) & 
               r_register.BP2  & r_register.BP1                 & 
               r_register.BP0  & r_register.WEL &
               r_register.WIP );
   end f_STD_LOGIC_VECTOR;


   -----------------------------------------------------------------------------
   --! @brief Translation from STD_LOGIC_VECTOR to r_CTR0 record
   --! @param r_register STD_LOGIC_VECTOR to be translated
   -----------------------------------------------------------------------------
   function f_CTR0  (signal r_register : in STD_LOGIC_VECTOR(7  downto 0))
                                                                return r_CTR0 is
      variable  v_CTR0 : r_CTR0;
   begin
      v_CTR0.OPA     := r_register(0);
      v_CTR0.OPF     := r_register(1);
      v_CTR0.x       := r_register(7 downto 2);
      return v_CTR0; 
   end f_CTR0;

   -----------------------------------------------------------------------------
   --! @brief Translation from STD_LOGIC_VECTOR to r_CTR1 record
   --! @param r_register STD_LOGIC_VECTOR to be translated
   -----------------------------------------------------------------------------
   function f_CTR1  (signal r_register : in STD_LOGIC_VECTOR(23 downto 0))
                                                                return r_CTR1 is
      variable  v_CTR1 : r_CTR1;
   begin
      v_CTR1.AWBF    := r_register(  c_NUMBER_OF_BUFFERS - 1  downto 0);
      v_CTR1.ARBF    := r_register(2*c_NUMBER_OF_BUFFERS - 1  downto
                                                            c_NUMBER_OF_BUFFERS );
      v_CTR1.CBSF    := r_register(3*c_NUMBER_OF_BUFFERS - 1  downto 
                                                          2*c_NUMBER_OF_BUFFERS );
      v_CTR1.PUSH_I  := r_register(6);
      v_CTR1.PUSH_A  := r_register(7);
      v_CTR1.PUSH_D  := r_register(8);
      v_CTR1.BOV     := r_register(9);
      v_CTR1.FS      := r_register(10);
      v_CTR1.CNTA    := unsigned(r_register(18 downto 11));
      v_CTR1.x       := r_register(23 downto 19);
      return v_CTR1; 
   end f_CTR1;

   -----------------------------------------------------------------------------
   --! @brief Translation from STD_LOGIC_VECTOR to r_BUF record
   --! @param r_register STD_LOGIC_VECTOR to be translated
   -----------------------------------------------------------------------------
   function f_BUF  (signal r_register : in STD_LOGIC_VECTOR(15  downto 0))
                                                                return r_BUF is
      variable  v_BUF : r_BUF;
   begin
      v_BUF.PAGE    := unsigned(r_register(7  downto 0));
      v_BUF.SECT    := unsigned(r_register(13 downto 8));
      v_BUF.y       := r_register(15 downto 14);
      return v_BUF; 
   end f_BUF;

   -----------------------------------------------------------------------------
   --! @brief Translation from STD_LOGIC_VECTOR to r_FMOH record
   --! @param r_register STD_LOGIC_VECTOR to be translated
   -----------------------------------------------------------------------------
   function f_FMOH(signal r_register : in STD_LOGIC_VECTOR(31  downto 0))
                                                                return r_FMOH is
      variable  v_FMOH : r_FMOH;
   begin
      v_FMOH.OP   := t_operations'val(to_integer(unsigned(
                                                       r_register(2 downto 0))));
      v_FMOH.OPP  := r_register(3);
      v_FMOH.CLBC := r_register(4);
      v_FMOH.FS   := r_register(5);
      v_FMOH.y    := r_register(14 downto  6);
      v_FMOH.PG   := r_register(22 downto 15);
      v_FMOH.SCT  := r_register(28 downto 23);
      v_FMOH.z    := r_register(31 downto 29);
      return v_FMOH; 
   end f_FMOH;

   -----------------------------------------------------------------------------
   --! @brief Translation from STD_LOGIC_VECTOR to r_SR_m25p32 record
   --! @param r_register STD_LOGIC_VECTOR to be translated
   -----------------------------------------------------------------------------
   function f_SR_m25p32 (signal r_register : in STD_LOGIC_VECTOR(7  downto 0))
   return r_SR_m25p32 is
      variable v_SR_m25p32 : r_SR_m25p32;
   begin
      v_SR_m25p32.WIP   := r_register(0);
      v_SR_m25p32.WEL   := r_register(1);
      v_SR_m25p32.BP0   := r_register(2);
      v_SR_m25p32.BP1   := r_register(3);
      v_SR_m25p32.BP2   := r_register(4);
      v_SR_m25p32.x     := r_register(6 downto 5);
      v_SR_m25p32.SRWD  := r_register(7);
      return v_SR_m25p32;
   end f_SR_m25p32;


   -----------------------------------------------------------------------------
   --! @brief Just shifting the buffers
   -----------------------------------------------------------------------------
   function f_SHIFT_BUFFERS (r_register : in r_CTR1;
                             shift      : in STD_LOGIC) return r_CTR1 is
      variable v_CTR1  : r_CTR1;
   begin
      v_CTR1 := r_register;
      if shift = '1' then
             --! First we update the AWBF field.
             v_CTR1.AWBF( r_register.AWBF'high downto r_register.AWBF'low + 1)
                                    := r_register.AWBF(
                          r_register.AWBF'high - 1 downto r_register.AWBF'low);
             v_CTR1.AWBF(0 + v_CTR1.AWBF'low) := r_register.AWBF(
                                       r_register.AWBF'high);
             --! Then we do the same for the ARBF one.
             v_CTR1.ARBF(r_register.ARBF'high downto r_register.ARBF'low + 1)
                                    := r_register.ARBF(r_register.ARBF'high
                                              - 1  downto r_register.ARBF'low);
             v_CTR1.ARBF(0 + v_CTR1.ARBF'low) := r_register.ARBF(
                                       r_register.ARBF'high);
      end if;
      return v_CTR1;
   end f_SHIFT_BUFFERS; 

   -----------------------------------------------------------------------------
   --! @brief Round-robin identification of free write and read buffers
   --         It acts as a circular buffer controlled by CNTA field. The shift
   --         is placed when r_CTR1.CNT reaches c_PAGE_SIZE/4 - 1 or r_CTR1.FS
   --         is set to '1'.
   --! @param r_register A r_register with information about free buffers
   -----------------------------------------------------------------------------
   function f_ACTIVE_BUFFERS (signal r_register : in r_CTR1) return r_CTR1 is
      variable v_CTR1           : r_CTR1;
      variable v_CTR1_AWBF_null : STD_LOGIC_VECTOR (c_NUMBER_OF_BUFFERS - 1
                                        downto 0) := (others => '0');
   begin
      v_CTR1 := r_register;
      if r_register.AWBF = v_CTR1_AWBF_null  then
         v_CTR1.AWBF                           := (others => '0');
         v_CTR1.AWBF(r_register.AWBF'low + 1)  := '1';
         v_CTR1.ARBF                           := (others => '0');
         v_CTR1.ARBF(r_register.ARBF'low)      := '1'; 
     else
         if (v_CTR1.CNTA = (c_PAGE_SIZE/4 - 1)) or (v_CTR1.FS = '1') then
            v_CTR1 := f_SHIFT_BUFFERS(v_CTR1, '1');
         end if;
      end if;
      v_CTR1.AWBF := v_CTR1.AWBF and not(r_register.CBSF);
      v_CTR1.ARBF := v_CTR1.ARBF and not(r_register.CBSF);
      return v_CTR1;
   end f_ACTIVE_BUFFERS;

end m25p32_pkg;
