----------------------------------------------------------------------------------
--
-- 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:    11:03:11 06/19/2012 
-- Design Name:     
-- Module Name:    spi_master_pkg - Behavioral 
-- Project Name:   CTDAH/SVEC
-- Target Devices: Spartan 6
-- Tool versions:  Xilinx ISE 13.4
-- Description:    Package providing all the register configurations for the
--                  SPI core.
--
-- Dependencies: 
--
-- Revision: 0.1
--   0.01          + File Created
--   0.1           - Alpha version. Able to write and output data correctly at
--                   different configurations.
-- Additional Comments: 
--
----------------------------------------------------------------------------------

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

package spi_master_pkg is


   attribute a_length : NATURAL;
   ----------------------------------------
   -- SPI0 register
   ----------------------------------------
   -- 32 bits
   -- Wishbone access: Write-read
   ----------------------------------------
   --  BIT        NAME         DESCRIPTION
   --      0       CPOL          Clock POLarity when idle
   --      1       CPHA          Clock PHAse
   --    4-2       BREAD         Bytes to be READ
   --   13-5       BDATA         Bytes of DATA to be sent        
   --   22-14      BADDR         Bytes of ADDRess to be sent  
   --   31-23      BINST         Bytes of INSTruction to be sent  
   ----------------------------------------
   type r_SPI0 is
      record
         CPOL     : STD_LOGIC;
         CPHA     : STD_LOGIC;
         BREAD    : UNSIGNED (4  downto  2);
         BDATA    : UNSIGNED (13 downto  5); 
         BADDR    : UNSIGNED (22 downto 14); 
         BINST    : UNSIGNED (31 downto 23); 
      end record;

   attribute a_length of r_SPI0 : type is 32;

   ----------------------------------------
   -- SPI1 register
   ----------------------------------------
   -- 32 bits
   -- Wishbone access: Write-read
   ----------------------------------------
   --  BIT        NAME         DESCRIPTION
   --    0         PUSH_DATA     PUSH DATA bytes into internal SPI
   --                             core memory 
   --    1         PUSH_ADDR     PUSH ADDRess bytes into internal SPI
   --                             core memory  
   --    2         PUSH_INST     PUSH INSTruction bytes into internal
   --                             SPI core memory 
   --    5-3       x             Reserved
   --    6         READ_MISO     READ from MISO line
   --    7         SEND_DATA     DATA bytes will be sent in a write operation 
   --    8         SEND_ADDR     ADDR bytes will be sent in a write operation
   --    9         SEND_INST     INST bytes will be sent in a write operation
   --    10        SEND_OP       perform a SEND OPeration
   --    11        y             Reserved
   --   15-12      CLK_DIV       CLocK DIVider
   --   31-16      z             Reserved
   ----------------------------------------
   type r_SPI1 is
      record
         PUSH_DATA    : STD_LOGIC; 
         PUSH_ADDR    : STD_LOGIC; 
         PUSH_INST    : STD_LOGIC;
         x            : STD_LOGIC_VECTOR(5 downto 3);
         READ_MISO    : STD_LOGIC;
         SEND_DATA    : STD_LOGIC;
         SEND_ADDR    : STD_LOGIC;
         SEND_INST    : STD_LOGIC;
         SEND_OP      : STD_LOGIC;
         y            : STD_LOGIC;
         CLK_DIV      : UNSIGNED (15 downto 12);
         z            : STD_LOGIC_VECTOR (31 downto 16);
      end record;

   attribute a_length of r_SPI1 : type is 32;

   ----------------------------------------
   -- SPI2 register
   ----------------------------------------
   -- 16 bits
   -- Wishbone access: Read-only
   ----------------------------------------
   --  BIT      NAME         DESCRIPTION
   --   0        MISO_DUP     MOSI Data UPdate
   --   1        READ_DONE    READ process DONE
   --   2        SENT_DATA    DATA was SENT 
   --   3        SENT_ADDR    ADDRess was SENT 
   --   4        SENT_INST    INSTruction was SENT
   --   5        SENT_OP      OPeration was SENT
   --   11-6     x            Reserved
   --   15-12    CLK_DIV      CLocK DIVision
   ----------------------------------------
   type r_SPI2 is
      record
         MISO_DUP     : STD_LOGIC;
         READ_DONE    : STD_LOGIC;
         SENT_DATA    : STD_LOGIC;
         SENT_ADDR    : STD_LOGIC;
         SENT_INST    : STD_LOGIC;
         SENT_OP      : STD_LOGIC;
         x            : STD_LOGIC_VECTOR (11 downto 6);
         CLK_DIV      : UNSIGNED (15 downto 12);
      end record;

   attribute a_length of r_SPI2 : type is 16;
   ----------------------------------------
   -- SPI3 register
   ----------------------------------------
   -- 32 bits
   -- Wishbone access: Read-only
   ----------------------------------------
   --  BIT        NAME         DESCRIPTION
   --   31-0       MOSI_DATA    DATA received from MOSI pin. 
   ----------------------------------------
   type r_SPI3 is
      record
         MOSI_DATA    : STD_LOGIC_VECTOR(31 downto 0);
      end record;

   attribute a_length of r_SPI3 : type is 32;

   constant c_INST_LENGTH          : NATURAL := 1; 
   constant c_ADDR_LENGTH          : NATURAL := 3;   
   constant c_DATA_LENGTH          : NATURAL := 256;
   constant c_READ_LENGTH          : NATURAL := 4;

   constant c_CLK_DIV   : UNSIGNED (15 downto 12) := to_unsigned( 2, 4);
   constant c_SPI0_default : r_SPI0 := (CPOL  => '0',
                                        CPHA  => '0',
                                        BREAD => to_unsigned(0, 3),
                                        BDATA => to_unsigned(0, 9),
                                        BADDR => to_unsigned(0, 9),
                                        BINST => to_unsigned(0, 9));
                                    --! BREAD => to_unsigned(c_READ_LENGTH,
                                    --!                      3),
                                    --! BDATA => to_unsigned(c_DATA_LENGTH,
                                    --!                      9),
                                    --! BADDR => to_unsigned(c_ADDR_LENGTH,
                                    --!                      9),
                                    --! BINST => to_unsigned(c_INST_LENGTH,
                                    --!                      9));

   constant c_SPI1_default : 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_CLK_DIV,
                                        z         => (others => '0'));
                                
   constant c_SPI2_default : r_SPI2 := (SENT_DATA => '0', 
                                        SENT_ADDR => '0', 
                                        SENT_INST => '0', 
                                        SENT_OP   => '0',
                                        MISO_DUP  => '0',
                                        READ_DONE => '0',
                                        x         => (others => '0'), 
                                        CLK_DIV   => (others => '0'));
                                
   constant c_CLK_DIVISION_LOGSIZE : NATURAL := 2;
   constant c_COUNTER_DATA_WIDTH   : NATURAL := 12;

   constant c_CLK_I_PERIOD         : NATURAL := 50;  --! ns
   constant c_CS_SU_SPI            : NATURAL := 20;  --! ns
--   constant c_CS_SU_TICKS          : NATURAL := c_CS_SU_SPI/c_CLK_I_PERIOD + 1;
   constant c_CS_HOLD_SPI          : NATURAL := 100; --! ns
--   constant c_CS_HOLD_TICKS        : NATURAL := c_CS_HOLD_SPI/c_CLK_I_PERIOD + 1;

   constant c_SPI0_addr  : STD_LOGIC_VECTOR (3  downto 0) := X"0"; 
   constant c_SPI1_addr  : STD_LOGIC_VECTOR (3  downto 0) := X"1"; 
   constant c_SPI2_addr  : STD_LOGIC_VECTOR (3  downto 0) := X"2"; 
   constant c_SPI3_addr  : STD_LOGIC_VECTOR (3  downto 0) := X"3"; 

   function f_SPI0  (signal r_register : in STD_LOGIC_VECTOR(31 downto 0))
                                                                 return r_SPI0;
   function f_SPI1  (signal r_register : in STD_LOGIC_VECTOR(31 downto 0))
                                                                 return r_SPI1;
   function f_SPI2  (signal r_register : in STD_LOGIC_VECTOR(15 downto 0))
                                                                 return r_SPI2;
   function f_SPI3  (signal r_register : in STD_LOGIC_VECTOR(31 downto 0))
                                                                 return r_SPI3;


   function f_STD_LOGIC_VECTOR(r_register : in r_SPI0) return STD_LOGIC_VECTOR;
   function f_STD_LOGIC_VECTOR(r_register : in r_SPI1) return STD_LOGIC_VECTOR;
   function f_STD_LOGIC_VECTOR(r_register : in r_SPI2) return STD_LOGIC_VECTOR;
   function f_STD_LOGIC_VECTOR(r_register : in r_SPI3) return STD_LOGIC_VECTOR;

   function f_read_edge(r_register : in r_SPI0;
                        spi_clk    : in STD_LOGIC;
                        spi_clk_d0 : in STD_LOGIC) return STD_LOGIC;

   function f_write_edge(r_register : in r_SPI0;
                         spi_clk    : in STD_LOGIC;
                         spi_clk_d0 : in STD_LOGIC) return STD_LOGIC;

   function f_min(a: NATURAL; b: NATURAL) return NATURAL; 
end spi_master_pkg;

package body spi_master_pkg is

   function f_min(a: NATURAL; b: NATURAL) return NATURAL is            
      variable ret: NATURAL;                                                       
   begin                                                                           
      if a < b then                                                                
         ret := a;                                                                 
      else                                                                         
         ret := b;                                                                 
      end if;
      return ret;
   end f_min;              

   -----------------------------------------------------------------------------
   --! @brief SPI0 record type translation to STD_LOGIC_VECTOR
   --! @param r_register r_SPI0 record type to be translated
   -----------------------------------------------------------------------------
   function f_STD_LOGIC_VECTOR  (r_register : in r_SPI0) return STD_LOGIC_VECTOR is
   begin
      return (  std_logic_vector(r_register.BINST)
              & std_logic_vector(r_register.BADDR)
              & std_logic_vector(r_register.BDATA)
              & std_logic_vector(r_register.BREAD)
              & r_register.CPHA 
              & r_register.CPOL); 
   end f_STD_LOGIC_VECTOR;


   -----------------------------------------------------------------------------
   --! @brief Translation from STD_LOGIC_VECTOR to r_SPI0 record
   --! @param r_register STD_LOGIC_VECTOR to be translated
   -----------------------------------------------------------------------------
   function f_SPI0  (signal r_register : in STD_LOGIC_VECTOR(31  downto 0)) return r_SPI0 is
      variable  v_SPI0 : r_SPI0;
   begin
      v_SPI0.CPOL    := r_register(0); 
      v_SPI0.CPHA    := r_register(1); 
      v_SPI0.BREAD   := unsigned(r_register( 4 downto  2)); 
      v_SPI0.BDATA   := unsigned(r_register(13 downto  5)); 
      v_SPI0.BADDR   := unsigned(r_register(22 downto 14)); 
      v_SPI0.BINST   := unsigned(r_register(31 downto 23)); 
      return v_SPI0;
   end f_SPI0;


   -----------------------------------------------------------------------------
   --! @brief SPI1 record type translation to STD_LOGIC_VECTOR
   --! @param r_register r_CTR0 record type to be translated
   -----------------------------------------------------------------------------
   function f_STD_LOGIC_VECTOR  (r_register : in r_SPI1) return STD_LOGIC_VECTOR is
   begin
      return (  r_register.z & std_logic_vector(r_register.CLK_DIV)
              & r_register.y
              & r_register.SEND_OP
              & r_register.SEND_INST & r_register.SEND_ADDR
              & r_register.SEND_DATA & r_register.READ_MISO  
              & r_register.x
              & r_register.PUSH_INST & r_register.PUSH_ADDR
              & r_register.PUSH_DATA
           ); 
   end f_STD_LOGIC_VECTOR;


   -----------------------------------------------------------------------------
   --! @brief Translation from STD_LOGIC_VECTOR to r_SPI1 record
   --! @param r_register STD_LOGIC_VECTOR to be translated
   -----------------------------------------------------------------------------
   function f_SPI1  (signal r_register : in STD_LOGIC_VECTOR(31  downto 0)) return r_SPI1 is
      variable  v_SPI1 : r_SPI1;
   begin
      v_SPI1.PUSH_DATA   := r_register(0); 
      v_SPI1.PUSH_ADDR   := r_register(1); 
      v_SPI1.PUSH_INST   := r_register(2); 
      v_SPI1.x           := r_register(5  downto 3); 
      v_SPI1.READ_MISO   := r_register(6);
      v_SPI1.SEND_DATA   := r_register(7); 
      v_SPI1.SEND_ADDR   := r_register(8); 
      v_SPI1.SEND_INST   := r_register(9); 
      v_SPI1.SEND_OP     := r_register(10);
      v_SPI1.y           := r_register(11); 
      v_SPI1.CLK_DIV     := unsigned(r_register(15 downto 12)); 
      v_SPI1.z           := r_register(31 downto 16); 
      return v_SPI1;
   end f_SPI1;

   -----------------------------------------------------------------------------
   --! @brief SPI2 record type translation to STD_LOGIC_VECTOR
   --! @param r_register r_SPI2 record type to be translated
   -----------------------------------------------------------------------------
   function f_STD_LOGIC_VECTOR  (r_register : in r_SPI2)
                                                      return STD_LOGIC_VECTOR is
   begin
      return (  std_logic_vector(r_register.CLK_DIV)
              & r_register.x
              & r_register.READ_DONE
              & r_register.MISO_DUP
              & r_register.SENT_OP
              & r_register.SENT_INST
              & r_register.SENT_ADDR
              & r_register.SENT_DATA); 
   end f_STD_LOGIC_VECTOR;


   -----------------------------------------------------------------------------
   --! @brief Translation from STD_LOGIC_VECTOR to r_SPI2 record
   --! @param r_register STD_LOGIC_VECTOR to be translated
   -----------------------------------------------------------------------------
   function f_SPI2  (signal r_register : in STD_LOGIC_VECTOR(15  downto 0))
                                                                return r_SPI2 is
      variable  v_SPI2 : r_SPI2;
   begin
      v_SPI2.SENT_DATA   := r_register(0); 
      v_SPI2.SENT_ADDR   := r_register(1); 
      v_SPI2.SENT_INST   := r_register(2); 
      v_SPI2.SENT_OP     := r_register(3);
      v_SPI2.MISO_DUP    := r_register(4);
      v_SPI2.READ_DONE   := r_register(5);
      v_SPI2.x           := r_register(11 downto 6);
      v_SPI2.CLK_DIV     := unsigned(r_register(15 downto 12)); 
      return v_SPI2;
   end f_SPI2;


   -----------------------------------------------------------------------------
   --! @brief SPI3 record type translation to STD_LOGIC_VECTOR
   --! @param r_register r_SPI3 record type to be translated
   -----------------------------------------------------------------------------
   function f_STD_LOGIC_VECTOR  (r_register : in r_SPI3)
                                                      return STD_LOGIC_VECTOR is
   begin
      return (  std_logic_vector(r_register.MOSI_DATA)); 
   end f_STD_LOGIC_VECTOR;


   -----------------------------------------------------------------------------
   --! @brief Translation from STD_LOGIC_VECTOR to r_SPI3 record
   --! @param r_register STD_LOGIC_VECTOR to be translated
   -----------------------------------------------------------------------------
   function f_SPI3  (signal r_register : in STD_LOGIC_VECTOR(31  downto 0))
                                                                return r_SPI3 is
      variable  v_SPI3 : r_SPI3;
   begin
      v_SPI3.MOSI_DATA   := r_register; 
      return v_SPI3;
   end f_SPI3;

   -----------------------------------------------------------------------------
   --! @brief Function that determines a read edge depending upon
   --!        CPOL and CPHA values.
   --! @param r_register  SPI0 register containing CPOL and CPHA 
   --!        spi_clk     STD_LOGIC containing currently sampled spi clock value
   --!        spi_clk_d0  STD_LOGIC containing previously sampled spi clock value
   -----------------------------------------------------------------------------
   function f_read_edge(r_register  : in r_SPI0;
                         spi_clk    : in STD_LOGIC;
                         spi_clk_d0 : in STD_LOGIC) return STD_LOGIC is
      variable read_edge : STD_LOGIC;
   begin
      read_edge       := '0';
      if (r_register.CPOL xor r_register.CPHA)  = '0' then
      --! The read case is the rising_edge
         if spi_clk = '1' and spi_clk_d0 = '0' then
            read_edge       := '1';
         end if;
      else
      --! The read case is the falling_edge
         if spi_clk = '0' and spi_clk_d0 = '1' then
            read_edge       := '1';
         end if;
      end if;
      return read_edge;
   end f_read_edge;

   -----------------------------------------------------------------------------
   --! @brief Function that determines a write edge depending upon
   --!        CPOL and CPHA values.
   --! @param r_register  SPI0 register containing CPOL and CPHA 
   --!        spi_clk     STD_LOGIC containing currently sampled spi clock value
   --!        spi_clk_d0  STD_LOGIC containing previously sampled spi clock value
   -----------------------------------------------------------------------------
   function f_write_edge(r_register  : in r_SPI0;
                         spi_clk    : in STD_LOGIC;
                         spi_clk_d0 : in STD_LOGIC) return STD_LOGIC is
      variable write_edge : STD_LOGIC;
   begin
      write_edge       := '0';
      if (r_register.CPOL xor r_register.CPHA)  = '0' then
      --! The write case is the falling_edge
         if spi_clk = '0' and spi_clk_d0 = '1' then
            write_edge       := '1';
         end if;
      else
      --! The write case is the rising_edge
         if spi_clk = '1' and spi_clk_d0 = '0' then
            write_edge       := '1';
         end if;
      end if;
      return write_edge;
   end f_write_edge;

end spi_master_pkg;
