--
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_arith.all;
use ieee.STD_LOGIC_UNSIGNED.all;

USE work.sercon_types.all;

--
entity sercon_master is
  generic(
    --    is_fwft: boolean:=true;
    bit_width: natural:=32
    );
  port(
    rst: in std_logic;
    clk: in std_logic;
    --
    busy: out std_logic;
    --
    -- fifo interface: 
    empty: in std_logic;
    rd_en: out std_logic;
    pdin: in std_logic_vector(bit_width-1 downto 0);
    --
    full: in std_logic;
    wr_en: out std_logic;
    pdout: out std_logic_vector(bit_width-1 downto 0);
    --
    -- serial interface:
    ctrl_bus: out sercon_ctrl;
    --
    src:out sercon_bus;
    ret: in sercon_bus

    );
end entity sercon_master;
--
architecture v0 of sercon_master is

  type sercon_master_t is(idle,
                          rd_m_addr,
                          rd_m_mask,
                          chk_m_data,
                          rd_m_data, 
                          shift_in, 
                          wr_m_data,
                          wr_done);
  
  signal sercon_state: sercon_master_t;
  
  signal int_addr: std_logic_vector(bit_width-1 downto 0);
  signal int_real_addr: std_logic_vector(bit_width-2 downto 0);
  signal int_data: std_logic_vector(bit_width-1 downto 0);
  signal int_mask: std_logic_vector(bit_width-1 downto 0);
  
  signal int_rd_en: std_logic;
  signal int_wr_en: std_logic;
  
  signal int_wr: std_logic;
  signal int_sclk_en: std_logic;

  signal int_wload: std_logic;
  signal int_rload: std_logic;

  signal sdin: std_logic;
  signal sdout: std_logic;
  signal smask: std_logic;
  
  constant wr_bit: std_logic:='1';
  signal int_bit_cnt: unsigned(7 downto 0);
  signal int_word_cnt: std_logic_vector(bit_width-2 downto 0);
begin
  int_real_addr<=int_addr(bit_width-2 downto 0);
  
  -- generate the busy indicator:
  busy<='0' when sercon_state=idle else '1';

  -- 
  rd_en <= int_rd_en when empty='0' else '0';
  wr_en <= int_wr_en when full='0' else '0';

  int_wr<=int_addr(bit_width-1);

  ctrl_bus.rst<=rst;
  ctrl_bus.clk<=clk;
  ctrl_bus.cen<=int_sclk_en;
  ctrl_bus.wload<=int_wload;
  ctrl_bus.rload<=int_rload;

  src.smask<=smask;
  src.sdata<=sdout;
  sdin<=ret.sdata;
  
--       sclk_en: out std_logic;
--     --
--     wload: out std_logic;
--     rload: out std_logic;
--     --
--     sdin: in std_logic;
--     sdout: out std_logic;
--     --
--     smask: out std_logic
    --



  
  manage_sbus:process(rst, clk)

  begin
    if(rst='1')then 
      sercon_state<=idle;

      int_rd_en<='0';
      int_wr_en<='0';
      
      int_bit_cnt<=conv_unsigned(bit_width,8);
      int_word_cnt<=conv_std_logic_vector(0, bit_width-1);

      int_wload<='0';
      int_rload<='0';

      int_addr<=(others=>'0');
      int_data<=(others=>'0');
      int_mask<=(others=>'0');
      int_sclk_en<='0';

      smask<='0';
      sdout<='0';
      
    elsif(clk='1' and clk'event)then

      case (sercon_state) is
        
        when idle =>
          int_sclk_en<='0';
          int_wload<='0';
          int_rload<='0';

          int_rd_en<='0';
          int_wr_en<='0';
     
          sercon_state<=idle;
          if(empty='0')then 
            sercon_state<=rd_m_addr;
            int_addr<=pdin;
            int_rd_en<='1';
          end if;

          int_bit_cnt<=conv_unsigned(bit_width,8);
          int_word_cnt<=conv_std_logic_vector(0, bit_width-1);
          
        when rd_m_addr =>
          int_rd_en<='0';

          if(empty='0')then 
            sercon_state<=rd_m_mask;
          end if;
          
          if(int_wr=not(wr_bit))then
            -- this is a read:
            -- first need to strobe the data into the
            -- return shift registers:
            int_rload<='1';
            sercon_state<=shift_in;
          end if;
          
        when rd_m_mask =>
          
          int_rd_en<='0';
          if(empty='0')then
            -- this is a write and the data are present:
            int_mask<=pdin;
            int_rd_en<='1';
            sercon_state<=chk_m_data;
          end if;

        when chk_m_data=>
          int_rd_en<='0';
          if(empty='0')then 
            sercon_state<=rd_m_data;
          end if;

        when rd_m_data =>
          int_rd_en<='0';
          if(empty='0')then
            -- this is a write and the data are present:
            int_data<=pdin;
            int_rd_en<='1';
            sercon_state<=shift_in;
          end if;
                    
        when shift_in =>
          int_rload<='0';
          int_rd_en<='0'; 

          int_sclk_en<='1';

          int_bit_cnt<=int_bit_cnt-1;
          if(int_bit_cnt=1)then
            int_bit_cnt<=conv_unsigned(bit_width,8); 
            int_word_cnt<=int_word_cnt+1;
          end if;
          
          if(int_real_addr=int_word_cnt)then
            int_sclk_en<='0';
            if(int_wr=wr_bit)then
              sercon_state<=wr_done;
            else
              sercon_state<=wr_m_data;
            end if;    
          end if;

          if(int_bit_cnt=1)then
            int_bit_cnt<=conv_unsigned(bit_width,8); 
          end if;

          -- this will take care of the data shifting in both directions:
          int_data<=int_data(bit_width-2 downto 0) & sdin;
          sdout<=int_data(bit_width-1);
          
          if(int_wr=wr_bit)then
            int_mask<=int_mask(bit_width-2 downto 0) & '0';
            smask<=int_mask(bit_width-1);
          end if;
        
        when wr_m_data =>
          if(full='0')then

            int_wr_en<='1';
            sercon_state<=idle;
          end if;
          
        when wr_done =>
          int_wload<='1';
          sercon_state<=idle;
                    
        when others=>
          
      end case;

    end if;
    
  end process manage_sbus;

  pdout<=int_data;


end architecture v0;


