----------------------------------------------------------------------------------
--
-- 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
-- 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


   type r_SPI_CFG is
      record
         CPOL                : STD_LOGIC; 
         CPHA                : STD_LOGIC;
         CLK_DIV             : UNSIGNED(15 downto 12);-- Taken from spi
      end record;

   --! According to datasheet, the working values are:
   --!   - {CPOL = '0', CPHA = '0'}
   --!   - {CPOL = '1', CPHA = '1'}

   constant c_SPI_CFG  : r_SPI_CFG := (CPOL     => '0', 
                                       CPHA     => '0',
                                       CLK_DIV  =>  to_unsigned(4, 4));

   --! General constants. TO BE ADJUST BY USER IF OTHER MEMORY IS USED
   --------------------------
   --  Defaults
   --------------------------
   --!    64 sectors
   --!    256 pages/sector
   --!    256 bytes = 64 words

   constant c_SECTOR_BITS           : NATURAL := 6;
   constant c_PAGES_PER_SECTOR_BITS : NATURAL := 8;
   constant c_BYTES_PER_PAGE_BITS   : NATURAL := 8;
   constant c_WORDS_PER_PAGE_BITS   : NATURAL := c_BYTES_PER_PAGE_BITS - 2;

   constant c_PAGE_SIZE             : NATURAL := 2**c_BYTES_PER_PAGE_BITS;

   --! The four constant below autoadjust to user modifications
   constant c_M25P32_ADDR_SIZE      : NATURAL := 3;
   constant c_ADDR_HEADER_PAD_SIZE  : NATURAL := c_M25P32_ADDR_SIZE*8
                                         - c_SECTOR_BITS
                                         - c_PAGES_PER_SECTOR_BITS
                                         - c_BYTES_PER_PAGE_BITS; 
   constant c_ADDR_HEADER_PAD : STD_LOGIC_VECTOR(c_ADDR_HEADER_PAD_SIZE - 1
                                                downto 0) := (others => '0');
   constant c_ADDR_FOOTER_PAD  : STD_LOGIC_VECTOR(c_BYTES_PER_PAGE_BITS - 1
                                                downto 0) := (others => '0');
   --! Mapping of the registers
   constant c_WB_ADDR_PADDING : STD_LOGIC_VECTOR(c_WORDS_PER_PAGE_BITS - 1
                                  downto 2) := (others => '0');
   ----------------------------------------
   --  Memory Map
   ----------------------------------------
   --  WBits
   ----------------------------------------
   --       [POSITION]            [CONTENT]
   --    0[WBits-2]00                 FMI 
   --    0[WBits-2]01                  SR
   --    0[WBits-2]10           READ_DATA
   --    1[WBits-2]00             WR_DATA
   --             ...                 ...                               
   --    1[WBits-2]00             WR_DATA
   ----------------------------------------
   constant c_FMI_addr        :STD_LOGIC_VECTOR (c_WORDS_PER_PAGE_BITS
                                  downto 0) :=
                                  '0' & c_WB_ADDR_PADDING & "00"; 
   constant c_SR_m25p32_addr  : STD_LOGIC_VECTOR (c_WORDS_PER_PAGE_BITS
                                  downto 0) :=
                                  '0' & c_WB_ADDR_PADDING & "01";
   constant c_DATA_READ_addr  : STD_LOGIC_VECTOR (c_WORDS_PER_PAGE_BITS
                                  downto 0) :=
                                  '0' & c_WB_ADDR_PADDING & "10";
   constant c_DATA_WRITE_addr : STD_LOGIC_VECTOR (c_WORDS_PER_PAGE_BITS
                                  downto 0) :=
                                  '1' & c_WB_ADDR_PADDING & "00";

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

   ----------------------------------------
   --  Operation Status Register
   ----------------------------------------
   --  BIT        NAME      DESCRIPTION        
   --   0          OPA       OPeration Attended
   --   1          OPF       Operation Finished
   --
   ----------------------------------------
   type r_OP is
      record
         OPA     : STD_LOGIC;
         OPF     : STD_LOGIC;
      end record;

   attribute a_length of r_OP      : type is 2;

   ----------------------------------------
   --  Flash Memory Instruction
   ----------------------------------------
   --  16 bits
   --  Wishbone access: Write-read
   ----------------------------------------
   --  BIT        NAME      DESCRIPTION        
   --   2-0        x         Reserved (from drives vp will be appended here OP) 
   --   5-3        OP        OPeration         
   --   13-6       PG        PaGe number       
   --   19-14      SCT       SeCTor number     
   --   23-20      y         Reserved          
   ----------------------------------------
   --  List of operations:
   --  0b000       x         Reserved
   --  0b001       RDID      ReaD IDentification
   --  0b010       RDSR      ReaD Status Register 
   --  0b011       WRSR      WRite Status Register
   --  0b100       READ      ReaD Page
   --  0b101       PP        Page Program
   --  0b110       SE        Sector Erase
   --  0b111       BE        Block Erase
   ----------------------------------------
   type t_operations is (x, RDID, RDSR, WRSR,
                         READ, PP, SE, BE); 

   type r_FMI is
      record
         OPR   : STD_LOGIC;
         x     : STD_LOGIC_VECTOR(2 downto 1);
         OP    : t_operations;
         PG    : UNSIGNED(c_PAGES_PER_SECTOR_BITS - 1 + 6 downto  6);
         SCT   : UNSIGNED(c_SECTOR_BITS + c_PAGES_PER_SECTOR_BITS
                                                          - 1 + 6 downto
                                                c_PAGES_PER_SECTOR_BITS + 6);
         y     : STD_LOGIC_VECTOR(23 downto c_SECTOR_BITS + c_PAGES_PER_SECTOR_BITS
                                                          + 6);
      end record;

   attribute a_length of r_FMI      : type is 24;


   ----------------------------------------
   -- 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;

   type r_word is
      record
         word_slv : STD_LOGIC_VECTOR(31 downto 0);
      end record;

   type r_page is array (c_PAGE_SIZE/4 - 1 downto 0) of r_word;

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

   constant c_OP_default   : r_OP := (OPA => '0',
                                      OPF => '0');

   --! Default: FMI no operation set by default
   constant c_FMI_default  : r_FMI := (OPR     => '0',
                                       x       => (others => '0'),
                                       OP      => x,
                                       PG      => (others => '0'),
                                       SCT     => (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');

   ------------------------------------------------------------------------------
   --  m25p32 m25p32 m25p32 m25p32 m25p32 SPI Instruction set
   ------------------------------------------------------------------------------
   --  INSTRUCTION        DESCRIPTION                ADDRESS         DATA
   --                                                 BYTES          BYTES
   --    WREN              WRite ENable                 0             0
   --    WRDI              WRite DIsable                0             0
   --    RDID              ReaD IDentification          0             3
   --    RDSR              ReaD Status Register         0             1
   --    WRSR              WRite Status Register        0             1
   --    READ              READ data bytes              3             4
   --    PP                Page Program                 3             256
   --    SE                Sector Erase                 3             0
   --    BE                Bulk Erase                   0             0
   -------------------------------------------------------------------------------
   constant c_WREN_inst      : STD_LOGIC_VECTOR(7  downto 0) := X"06";
   constant c_WRDI_inst      : STD_LOGIC_VECTOR(7  downto 0) := X"04";
   constant c_RDID_inst      : STD_LOGIC_VECTOR(7  downto 0) := X"9F";
   constant c_RDSR_inst      : STD_LOGIC_VECTOR(7  downto 0) := X"05";
   constant c_WRSR_inst      : STD_LOGIC_VECTOR(7  downto 0) := X"01";
   constant c_READ_inst      : STD_LOGIC_VECTOR(7  downto 0) := X"03";
   --   constant c_FAST_READ_inst : STD_LOGIC_VECTOR(7  downto 0) := X"0B";
   constant c_PP_inst        : STD_LOGIC_VECTOR(7  downto 0) := X"02";
   constant c_SE_inst        : STD_LOGIC_VECTOR(7  downto 0) := X"D8";
   constant c_BE_inst        : STD_LOGIC_VECTOR(7  downto 0) := X"C7";

   constant c_SPI0_IDLE  : r_SPI0 := (CPOL  => c_SPI_CFG.CPOL,
                                      CPHA  => c_SPI_CFG.CPHA,
                                      BREAD => (others => '0'),
                                      BDATA => (others => '0'),
                                      BADDR => (others => '0'),
                                      BINST => (others => '0'));

      constant c_SPI1_IDLE : r_SPI1 := (PUSH_DATA => '0', PUSH_ADDR => '0',
                                        PUSH_INST => '0', x => (others  => '0'),
                                        READ_MISO => '0',
                                        SEND_DATA => '0',
                                        SEND_ADDR => '0',
                                        SEND_INST => '0',
                                        SEND_OP   => '0',
                                        y         => '0',
                                        CLK_DIV   => c_SPI_CFG.CLK_DIV,
                                        z         => (others  => '0'));


   --! WREN instruction. Configuration for SPI0 and SPI1 registers.
   constant c_SPI0_WREN  : r_SPI0 := (CPOL  => c_SPI_CFG.CPOL,
                                      CPHA  => c_SPI_CFG.CPHA,
                                      BREAD => (others => '0'),
                                      BDATA => (others => '0'),
                                      BADDR => (others => '0'),
                                      BINST => to_unsigned( 1 , 9));

      constant c_SPI1_WREN : r_SPI1 := (PUSH_DATA => '0', PUSH_ADDR => '0',
                                        PUSH_INST => '1', x => (others  => '0'),
                                        READ_MISO => '0',
                                        SEND_DATA => '0',
                                        SEND_ADDR => '0',
                                        SEND_INST => '1',
                                        SEND_OP   => '1',
                                        y         => '0',
                                        CLK_DIV   => c_SPI_CFG.CLK_DIV,
                                        z         => (others  => '0'));


   --! WRDI instruction. Configuration for SPI0 and SPI1 registers.
   constant c_SPI0_WRDI  : r_SPI0 := (CPOL  => c_SPI_CFG.CPOL,
                                      CPHA  => c_SPI_CFG.CPHA,
                                      BREAD => (others => '0'),
                                      BDATA => (others => '0'),
                                      BADDR => (others => '0'),
                                      BINST => to_unsigned( 1, 9));

      constant c_SPI1_WRDI : r_SPI1 := (PUSH_DATA => '0', PUSH_ADDR => '0',
                                        PUSH_INST => '1', x => (others  => '0'),
                                        READ_MISO => '0',
                                        SEND_DATA => '0',
                                        SEND_ADDR => '0',
                                        SEND_INST => '1',
                                        SEND_OP   => '1',
                                        y         => '0',
                                        CLK_DIV   => c_SPI_CFG.CLK_DIV,
                                        z         => (others  => '0'));


   --! RDID instruction. Configuration for SPI0 and SPI1 registers.
   constant c_SPI0_RDID  : r_SPI0 := (CPOL  => c_SPI_CFG.CPOL,
                                      CPHA  => c_SPI_CFG.CPHA,
                                      BREAD => to_unsigned( 3, 3),
                                      BDATA => (others => '0'),
                                      BADDR => (others => '0'),
                                      BINST => to_unsigned( 1, 9));

      constant c_SPI1_RDID : r_SPI1 := (PUSH_DATA => '0', PUSH_ADDR => '0',
                                        PUSH_INST => '1', x => (others  => '0'),
                                        READ_MISO => '1',
                                        SEND_DATA => '0',
                                        SEND_ADDR => '0',
                                        SEND_INST => '1',
                                        SEND_OP   => '1',
                                        y         => '0',
                                        CLK_DIV   => c_SPI_CFG.CLK_DIV,
                                        z         => (others  => '0'));


   --! SRWD instruction. Configuration for SPI0 and SPI1 registers.
   constant c_SPI0_RDSR  : r_SPI0 := (CPOL  => c_SPI_CFG.CPOL,
                                      CPHA  => c_SPI_CFG.CPHA,
                                      BREAD => to_unsigned( 1, 3),
                                      BDATA => (others => '0'),
                                      BADDR => (others => '0'),
                                      BINST => to_unsigned( 1, 9));

      constant c_SPI1_RDSR : r_SPI1 := (PUSH_DATA => '0', PUSH_ADDR => '0',
                                        PUSH_INST => '1', x => (others  => '0'),
                                        READ_MISO => '1',
                                        SEND_DATA => '0',
                                        SEND_ADDR => '0',
                                        SEND_INST => '1',
                                        SEND_OP   => '1',
                                        y         => '0',
                                        CLK_DIV   => c_SPI_CFG.CLK_DIV,
                                        z         => (others  => '0'));


   --! WRSR instruction. Configuration for SPI0 and SPI1 registers.
   constant c_SPI0_WRSR  : r_SPI0 := (CPOL  => c_SPI_CFG.CPOL,
                                      CPHA  => c_SPI_CFG.CPHA,
                                      BREAD => (others => '0'),
                                      BDATA => to_unsigned( 1 , 9),
                                      BADDR => (others => '0'),
                                      BINST => to_unsigned( 1 , 9));

      constant c_SPI1_WRSR : r_SPI1 := (PUSH_DATA => '1', PUSH_ADDR => '0',
                                        PUSH_INST => '1', x => (others  => '0'),
                                        READ_MISO => '0',
                                        SEND_DATA => '1',
                                        SEND_ADDR => '0',
                                        SEND_INST => '1',
                                        SEND_OP   => '1',
                                        y         => '0',
                                        CLK_DIV   => c_SPI_CFG.CLK_DIV,
                                        z         => (others  => '0'));


   --! READ instruction. Configuration for SPI0 and SPI1 registers.
   constant c_SPI0_READ  : r_SPI0 := (CPOL  => c_SPI_CFG.CPOL,
                                      CPHA  => c_SPI_CFG.CPHA,
                                      --! Aribtrary value of 4.
                                      --! According to manual it reads the
                                      --! whole memory.
                                      BREAD => to_unsigned( 4, 3),
                                      BDATA => to_unsigned( 0, 9),
                                      BADDR => to_unsigned( 3, 9),
                                      BINST => to_unsigned( 1, 9));

      constant c_SPI1_READ : r_SPI1 := (PUSH_DATA => '0', PUSH_ADDR => '1',
                                        PUSH_INST => '1', x => (others  => '0'),
                                        READ_MISO => '1',
                                        SEND_DATA => '0',
                                        SEND_ADDR => '1',
                                        SEND_INST => '1',
                                        SEND_OP   => '1',
                                        y         => '0',
                                        CLK_DIV   => c_SPI_CFG.CLK_DIV,
                                        z         => (others  => '0'));


   --! PP instruction. Configuration for SPI0 and SPI1 registers.
   constant c_SPI0_PP    : r_SPI0 := (CPOL  => c_SPI_CFG.CPOL,
                                      CPHA  => c_SPI_CFG.CPHA,
                                      BREAD => (others => '0'),
                                      BDATA => to_unsigned( 256 , 9),
                                      BADDR => to_unsigned(   3 , 9),
                                      BINST => to_unsigned(   1 , 9));

      constant c_SPI1_PP   : r_SPI1 := (PUSH_DATA => '1', PUSH_ADDR => '1',
                                        PUSH_INST => '1', x => (others  => '0'),
                                        READ_MISO => '0',
                                        SEND_DATA => '1',
                                        SEND_ADDR => '1',
                                        SEND_INST => '1',
                                        SEND_OP   => '1',
                                        y         => '0',
                                        CLK_DIV   => c_SPI_CFG.CLK_DIV,
                                        z         => (others  => '0'));


   --! SE instruction. Configuration for SPI0 and SPI1 registers.
   constant c_SPI0_SE    : r_SPI0 := (CPOL  => c_SPI_CFG.CPOL,
                                      CPHA  => c_SPI_CFG.CPHA,
                                      BREAD => (others => '0'),
                                      BDATA => (others => '0'),
                                      BADDR => to_unsigned( 3 , 9), 
                                      BINST => to_unsigned( 1 , 9));

      constant c_SPI1_SE   : r_SPI1 := (PUSH_DATA => '0', PUSH_ADDR => '1',
                                        PUSH_INST => '1', x => (others  => '0'),
                                        READ_MISO => '0',
                                        SEND_DATA => '0',
                                        SEND_ADDR => '1',
                                        SEND_INST => '1',
                                        SEND_OP   => '1',
                                        y         => '0',
                                        CLK_DIV   => c_SPI_CFG.CLK_DIV,
                                        z         => (others  => '0'));

   --! BE instruction. Configuration for SPI0 and SPI1 registers.
   constant c_SPI0_BE    : r_SPI0 := (CPOL  => c_SPI_CFG.CPOL,
                                      CPHA  => c_SPI_CFG.CPHA,
                                      BREAD => (others => '0'),
                                      BDATA => (others => '0'),
                                      BADDR => (others => '0'), 
                                      BINST => to_unsigned( 1 , 9));

      constant c_SPI1_BE   : r_SPI1 := (PUSH_DATA => '0', PUSH_ADDR => '0',
                                        PUSH_INST => '1', x => (others  => '0'),
                                        READ_MISO => '0',
                                        SEND_DATA => '0',
                                        SEND_ADDR => '0',
                                        SEND_INST => '1',
                                        SEND_OP   => '1',
                                        y         => '0',
                                        CLK_DIV   => c_SPI_CFG.CLK_DIV,
                                        z         => (others  => '0'));


   --! Included here to be less verbose in m25p32_core.vhd
   -- c_CLK_I_PERIOD can be found in spi_master_pkg.vhd
   component spi_master_core is
      generic(
        g_INST_LENGTH  : NATURAL := c_INST_LENGTH;
        g_ADDR_LENGTH  : NATURAL := c_ADDR_LENGTH;
        g_DATA_LENGTH  : NATURAL := c_DATA_LENGTH;
        g_CLK_I_PERIOD : NATURAL := c_CLK_I_PERIOD);
      port(
        rst_i        : in  STD_LOGIC;
        clk_i        : in  STD_LOGIC;

        inst_i       : in  STD_LOGIC_VECTOR (8*g_INST_LENGTH - 1 downto 0);
        addr_i       : in  STD_LOGIC_VECTOR (8*g_ADDR_LENGTH - 1 downto 0);
        data_i       : in  STD_LOGIC_VECTOR (8*g_DATA_LENGTH - 1 downto 0);

        SPI0_i       : in  STD_LOGIC_VECTOR (r_SPI0'a_length - 1 downto 0);
        SPI1_i       : in  STD_LOGIC_VECTOR (r_SPI1'a_length - 1 downto 0);
        SPI2_o       : out STD_LOGIC_VECTOR (r_SPI2'a_length - 1 downto 0);
        SPI3_o       : out STD_LOGIC_VECTOR (r_SPI3'a_length - 1 downto 0);

        rd_SPI3_o    : out STD_LOGIC;

        spi_mosi_o   : out STD_LOGIC;
        spi_miso_i   : in  STD_LOGIC;
        spi_clk_o    : out STD_LOGIC;
        spi_cs_n_o   : out STD_LOGIC);
   end component;

   ---------------------------------------------------------------------------
   --! Translation functions back and forth STD_LOGIC_VECTOR
   ---------------------------------------------------------------------------
   function  f_STD_LOGIC_VECTOR  (operation  : t_operations)
                                                      return STD_LOGIC_VECTOR;
   function  f_STD_LOGIC_VECTOR  (r_register : r_OP)  return STD_LOGIC_VECTOR;
   function  f_STD_LOGIC_VECTOR  (r_register : r_FMI) return STD_LOGIC_VECTOR;
   function  f_STD_LOGIC_VECTOR  (r_register : r_SR_m25p32)
                                                      return STD_LOGIC_VECTOR;
   function  f_STD_LOGIC_VECTOR  (word       : r_word)
                                                      return STD_LOGIC_VECTOR;
   function  f_STD_LOGIC_VECTOR  (page       : r_page)
                                                      return STD_LOGIC_VECTOR;

   function  f_operations(operation_slv : STD_LOGIC_VECTOR(2 downto 0))
                                                           return t_operations;
   function  f_OP (r_register : STD_LOGIC_VECTOR(1 downto 0))      return r_OP;
   function  f_FMI(r_register : STD_LOGIC_VECTOR(23  downto 0))   return r_FMI;
   function  f_SR_m25p32 (signal r_register : in STD_LOGIC_VECTOR(7  downto 0))
                                                            return r_SR_m25p32;
   function  f_word (signal page : in r_page;
                     signal pos  : in UNSIGNED(c_WORDS_PER_PAGE_BITS -1 downto 0))
                                                                 return r_word;
   function  f_page (signal page : in STD_LOGIC_VECTOR(c_PAGE_SIZE*8 -1 downto 0))
                                                                 return r_page;


end m25p32_pkg;

package body m25p32_pkg is

   -----------------------------------------------------------------------------
   --! @brief t_operations enumeration type translation to STD_LOGIC_VECTOR
   --! @param operation t_operations enum type to be translated
   -----------------------------------------------------------------------------
   function f_STD_LOGIC_VECTOR  (operation  : t_operations)
                                                     return STD_LOGIC_VECTOR is
      variable v_return : STD_LOGIC_VECTOR(2 downto 0);
   begin
      case operation is
         when RDID     =>
            v_return := STD_LOGIC_VECTOR(to_unsigned(1, 3));
         when RDSR     => 
            v_return := STD_LOGIC_VECTOR(to_unsigned(2, 3));
         when WRSR     => 
            v_return := STD_LOGIC_VECTOR(to_unsigned(3, 3));
         when READ     => 
            v_return := STD_LOGIC_VECTOR(to_unsigned(4, 3));
         when PP       => 
            v_return := STD_LOGIC_VECTOR(to_unsigned(5, 3));
         when SE       => 
            v_return := STD_LOGIC_VECTOR(to_unsigned(6, 3));
         when BE       => 
            v_return := STD_LOGIC_VECTOR(to_unsigned(7, 3));
         when others   =>
            v_return := STD_LOGIC_VECTOR(to_unsigned(0, 3));
      end case;
      return v_return;
   end f_STD_LOGIC_VECTOR;

   -----------------------------------------------------------------------------
   --! @brief Translation from STD_LOGIC_VECTOR to t_operations enum
   --! @param operation_slv STD_LOGIC_VECTOR to be translated
   -----------------------------------------------------------------------------
   function f_operations(operation_slv : STD_LOGIC_VECTOR(2 downto 0))
                                                      return t_operations is
      variable v_operations         : t_operations;
      variable v_operation_unsigned : UNSIGNED(2 downto 0) :=
                                                      UNSIGNED(operation_slv);
   begin
      case to_integer(v_operation_unsigned) is
         when 1 =>
            v_operations := RDID;
         when 2 =>
            v_operations := RDSR;
         when 3 =>
            v_operations := WRSR;
         when 4 =>
            v_operations := READ;
         when 5 =>
            v_operations := PP;
         when 6 =>
            v_operations := SE;
         when 7 =>
            v_operations := BE;
         when others =>
            v_operations := x;
      end case;
      return v_operations;
   end f_operations;


   function  f_STD_LOGIC_VECTOR  (r_register : r_OP) return STD_LOGIC_VECTOR is
   begin
      return   r_register.OPF 
             & r_register.OPA; 
   end f_STD_LOGIC_VECTOR;

   function  f_OP(r_register  : STD_LOGIC_VECTOR(1 downto 0)) return r_OP is
      variable v_return  : r_OP;
   begin
      v_return.OPA := r_register(0);
      v_return.OPF := r_register(1);
      return v_return;
   end function f_OP;


   -----------------------------------------------------------------------------
   --! @brief Procedure for translating r_FMI GROUP A write bits into a signal
   --! @param r_register r_FMI record type to be translated, bounded to group A
   --!                   write bits.
   --! @param s_out_A    Singal in which to be updated the r_FMI bits 
   -----------------------------------------------------------------------------
   function f_STD_LOGIC_VECTOR (r_register : r_FMI) return STD_LOGIC_VECTOR is
      variable v_OP_slv : STD_LOGIC_VECTOR(2 downto 0);
   begin
         v_OP_slv := f_STD_LOGIC_VECTOR(r_register.OP);
      return    r_register.y
              & STD_LOGIC_VECTOR(r_register.SCT)
              & STD_LOGIC_VECTOR(r_register.PG) 
              & v_OP_slv
              & r_register.x
              & r_register.OPR;
   end f_STD_LOGIC_VECTOR;

   -----------------------------------------------------------------------------
   --! @brief Translation from STD_LOGIC_VECTOR to r_FMI record
   --! @param r_register STD_LOGIC_VECTOR to be translated
   -----------------------------------------------------------------------------
   function f_FMI(r_register : STD_LOGIC_VECTOR(23  downto 0))
                                                                return r_FMI is
      variable v_FMI : r_FMI;
   begin
      v_FMI.OPR := r_register(0);
      v_FMI.x   := r_register(r_OP'a_length - 1 + 1 downto 1);
      v_FMI.OP  := f_operations(r_register(5 downto 3)); 
      v_FMI.PG  := UNSIGNED(r_register(c_PAGES_PER_SECTOR_BITS - 1 + 6 downto
                                                                          6)); 
      v_FMI.SCT := UNSIGNED(r_register(c_SECTOR_BITS + c_PAGES_PER_SECTOR_BITS
                                                          - 1 + 6 downto
                                                c_PAGES_PER_SECTOR_BITS + 6));
      v_FMI.y   := r_register(23 downto c_SECTOR_BITS + c_PAGES_PER_SECTOR_BITS
                                                          + 6);
      return v_FMI;
   end f_FMI;

   -----------------------------------------------------------------------------
   --! @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_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 r_word record type translation to STD_LOGIC_VECTOR
   --! @param word r_word record type to be translated
   -----------------------------------------------------------------------------
   function f_STD_LOGIC_VECTOR  (word       : r_word) return STD_LOGIC_VECTOR is
   begin
      return word.word_slv;
   end function;

   -----------------------------------------------------------------------------
   --! @brief Translation from STD_LOGIC_VECTOR to f_word record
   --! @param page Page to retrieve the word from.
   --! @param pos  Position of the word to be retrieved.
   -----------------------------------------------------------------------------
   function f_word (signal page : in r_page;
                    signal pos  : in UNSIGNED(c_WORDS_PER_PAGE_BITS -1 downto 0))
                   return r_word is
   begin
      return page(to_integer(pos));
   end function f_word;

   -----------------------------------------------------------------------------
   --! @brief r_page record type translation to STD_LOGIC_VECTOR
   --! @param page r_page record type to be translated
   -----------------------------------------------------------------------------
   function f_STD_LOGIC_VECTOR  (page       : r_page) return STD_LOGIC_VECTOR is
      variable v_page_slv : STD_LOGIC_VECTOR(c_PAGE_SIZE/4*32 - 1 downto 0);
      variable v_word_tmp : r_word;
   begin
      for i in 0 to c_PAGE_SIZE/4 - 1  loop
         v_word_tmp := page(i);
         v_page_slv(32*(i+1) - 1 downto 32*i) := f_STD_LOGIC_VECTOR(v_word_tmp);
      end loop;
      return v_page_slv;
   end function f_STD_LOGIC_VECTOR;

   -----------------------------------------------------------------------------
   --! @brief Translation from STD_LOGIC_VECTOR to r_page record
   --! @param page STD_LOGIC_VECTOR to be translated
   -----------------------------------------------------------------------------
   function f_page (signal page : in STD_LOGIC_VECTOR(c_PAGE_SIZE*8 -1 downto 0))
                   return r_page is
      variable v_page : r_page;
   begin
      for i in 0 to c_PAGE_SIZE/4 - 1 loop
         v_page(i).word_slv := page(32*(i+1) - 1 downto 32*i);
      end loop;
      return v_page;
   end function f_page;

end m25p32_pkg;
