OpenCores
URL https://opencores.org/ocsvn/mips_enhanced/mips_enhanced/trunk

Subversion Repositories mips_enhanced

[/] [mips_enhanced/] [trunk/] [grlib-gpl-1.0.19-b3188/] [lib/] [gaisler/] [memctrl/] [spimctrl.vhd] - Rev 2

Compare with Previous | Blame | View Log

------------------------------------------------------------------------------
--  This file is a part of the GRLIB VHDL IP LIBRARY
--  Copyright (C) 2003, Gaisler Research
--
--  This program is free software; you can redistribute it and/or modify
--  it under the terms of the GNU General Public License as published by
--  the Free Software Foundation; either version 2 of the License, or
--  (at your option) any later version.
--
--  This program is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--  GNU General Public License for more details.
--
--  You should have received a copy of the GNU General Public License
--  along with this program; if not, write to the Free Software
--  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
-------------------------------------------------------------------------------
-- Entity:      spimctrl
-- File:        spimctrl.vhd
-- Author:      Jan Andersson - Gaisler Research AB
--              jan@gaisler.com
-- Description: SPI flash memory controller. Supports a wide range of SPI
--              memory devices with the data read instruction configurable via
--              generics. Also has limited support for initializing and reading
--              SD Cards in SPI mode.
-- 
-- The controller has two memory areas. The flash area where the flash memory
-- is directly mapped and the I/O area where core registers are mapped.
--
-------------------------------------------------------------------------------
 
library ieee;
use ieee.std_logic_1164.all;
library grlib;
use grlib.amba.all;
use grlib.devices.all;
use grlib.stdlib.all;
library gaisler;
use gaisler.memctrl.all;
 
entity spimctrl is
  generic (
    hindex     : integer := 0;            -- AHB slave index
    hirq       : integer := 0;            -- Interrupt line
    faddr      : integer := 16#000#;      -- Flash map base address
    fmask      : integer := 16#fff#;      -- Flash area mask
    ioaddr     : integer := 16#000#;      -- I/O base address
    iomask     : integer := 16#fff#;      -- I/O mask
    spliten    : integer := 0;            -- AMBA SPLIT support
    oepol      : integer := 0;            -- Output enable polarity
    sdcard     : integer range 0 to 1   := 0; -- Core is connected to SD card
    readcmd    : integer range 0 to 255 := 16#0B#;  -- Mem. dev. READ command
    dummybyte  : integer range 0 to 1   := 1;  -- Dummy byte after cmd
    dualoutput : integer range 0 to 1   := 0;  -- Enable dual output
    scaler     : integer range 1 to 512 := 1; -- SCK scaler
    altscaler  : integer range 1 to 512 := 1; -- Alternate SCK scaler 
    pwrupcnt   : integer  := 0                -- System clock cycles to init
  );
  port (
    rstn    : in  std_ulogic;
    clk     : in  std_ulogic;
    ahbsi   : in  ahb_slv_in_type;
    ahbso   : out ahb_slv_out_type;
    spii    : in  spimctrl_in_type;
    spio    : out spimctrl_out_type
  );
end spimctrl;
 
architecture rtl of spimctrl is
 
  constant REVISION : amba_version_type := 0;
 
  constant HCONFIG : ahb_config_type := (
    0 => ahb_device_reg(VENDOR_GAISLER, GAISLER_SPIMCTRL, 0, REVISION, hirq),
    4 => ahb_iobar(ioaddr, iomask),
    5 => ahb_membar(faddr, '0', '0', fmask),
    others => zero32);
 
  -- BANKs
  constant CTRL_BANK  : integer := 0;
  constant FLASH_BANK : integer := 1;
 
  -----------------------------------------------------------------------------
  -- SD card constants
  -----------------------------------------------------------------------------
 
  constant SD_BLEN : integer := 4;
 
  constant SD_CRC_BYTE : std_logic_vector(7 downto 0) := X"95";
 
  constant SD_BLOCKLEN : std_logic_vector(31 downto 0) :=
    conv_std_logic_vector(SD_BLEN, 32);
 
  -- Commands
  constant SD_CMD0   : std_logic_vector(5 downto 0) := "000000";
  constant SD_CMD16  : std_logic_vector(5 downto 0) := "010000";
  constant SD_CMD17  : std_logic_vector(5 downto 0) := "010001";
  constant SD_CMD55  : std_logic_vector(5 downto 0) := "110111";
  constant SD_ACMD41 : std_logic_vector(5 downto 0) := "101001";
 
  -- Command timeout
  constant SD_CMD_TIMEOUT : integer := 100;
 
  -- Data token timeout
  constant SD_DATATOK_TIMEOUT : integer := 312500;
 
  -----------------------------------------------------------------------------
  -- SPI device constants
  -----------------------------------------------------------------------------
 
  -- Length of read instruction argument-1 
  constant SPI_ARG_LEN : integer := 2 + dummybyte;
 
  -----------------------------------------------------------------------------
  -- Core constants
  -----------------------------------------------------------------------------
 
  -- OEN
  constant OUTPUT : std_ulogic := conv_std_logic(oepol = 1);  -- Enable outputs
  constant INPUT : std_ulogic := not OUTPUT;   -- Tri-state outputs
 
  -- Register offsets
  constant CONF_REG_OFF  : std_logic_vector(7 downto 2) := "000000";
  constant CTRL_REG_OFF  : std_logic_vector(7 downto 2) := "000001";
  constant STAT_REG_OFF  : std_logic_vector(7 downto 2) := "000010";
  constant RX_REG_OFF    : std_logic_vector(7 downto 2) := "000011";
  constant TX_REG_OFF    : std_logic_vector(7 downto 2) := "000100";
 
  constant SPI_HSIZE_BYTE  : std_logic_vector(1 downto 0) := "00";
  constant SPI_HSIZE_HWORD : std_logic_vector(1 downto 0) := "01";
  constant SPI_HSIZE_WORD  : std_logic_vector(1 downto 0) := "10";
 
  -----------------------------------------------------------------------------
  -- Subprograms
  -----------------------------------------------------------------------------  
  -- Description: Determines required size of timer used for clock scaling
  function timer_size
    return integer is
  begin  -- timer_size
    if altscaler > scaler then
      return altscaler;
    end if;
    return scaler;
  end timer_size;
 
  -- Description: Returns the number of bits required for the haddr vector to
  -- be able to save the Flash area address.
  function req_addr_bits
    return integer is
  begin  -- req_addr_bits
    case fmask is
      when 16#fff# => return 20;
      when 16#ffe# => return 21;
      when 16#ffc# => return 22;
      when 16#ff8# => return 23;
      when 16#ff0# => return 24;
      when 16#fe0# => return 25;
      when 16#fc0# => return 26;
      when 16#f80# => return 27;
      when 16#f00# => return 28;
      when 16#e00# => return 29;
      when 16#c00# => return 30;
      when others  => return 31;
    end case;
  end req_addr_bits;
 
  -- Description: Returns true if SCK clock should transition
  function sck_toggle (
    curr         : std_logic_vector((timer_size-1) downto 0);
    last         : std_logic_vector((timer_size-1) downto 0);
    usealtscaler : boolean)
    return boolean is
  begin  -- sck_toggle
    if usealtscaler then
      return (curr(altscaler-1) xor last(altscaler-1)) = '1';
    end if;
    return (curr(scaler-1) xor last(scaler-1)) = '1';
  end sck_toggle;
 
  -- Description: Short for conv_std_logic_vector, avoiding an alias
  function cslv (
    i : integer;
    w : integer)
    return std_logic_vector is
  begin  -- cslv
    return conv_std_logic_vector(i,w);
  end cslv;
 
  -----------------------------------------------------------------------------
  -- States
  -----------------------------------------------------------------------------
 
  -- Main FSM states
  type spimstate_type is (IDLE, AHB_RESPOND, USER_SPI, BUSY);
 
--   subtype spimstate_type is std_logic_vector(1 downto 0);
 
--   constant IDLE        : std_logic_vector(spimstate_type'range) := "00";
--   constant AHB_RESPOND : std_logic_vector(spimstate_type'range) := "01";
--   constant USER_SPI    : std_logic_vector(spimstate_type'range) := "10";
--   constant BUSY        : std_logic_vector(spimstate_type'range) := "11";
 
  -- SPI device FSM states
 type spistate_type is (SPI_PWRUP,  SPI_READY, SPI_READ, SPI_ADDR, SPI_DATA);
 
--   subtype spistate_type is std_logic_vector(2 downto 0);
 
--   constant SPI_PWRUP : std_logic_vector(spistate_type'range) := "000";
--   constant SPI_READY : std_logic_vector(spistate_type'range) := "001";
--   constant SPI_READ  : std_logic_vector(spistate_type'range) := "010";
--   constant SPI_ADDR  : std_logic_vector(spistate_type'range) := "011";
--   constant SPI_DATA  : std_logic_vector(spistate_type'range) := "100";
 
  -- SD FSM states
  type sdstate_type is (SD_CHECK_PRES, SD_PWRUP0, SD_PWRUP1, SD_INIT_IDLE,
                        SD_ISS_ACMD41, SD_CHECK_CMD16, SD_READY,
                        SD_CHECK_CMD17, SD_CHECK_TOKEN, SD_HANDLE_DATA,
                        SD_SEND_CMD, SD_GET_RESP);
 
--   subtype sdstate_type is std_logic_vector(3 downto 0);
 
--   constant SD_CHECK_PRES   : std_logic_vector(sdstate_type'range) := "0000";
--   constant SD_PWRUP0       : std_logic_vector(sdstate_type'range) := "0001";
--   constant SD_PWRUP1       : std_logic_vector(sdstate_type'range) := "0010";
--   constant SD_INIT_IDLE    : std_logic_vector(sdstate_type'range) := "0011";
--   constant SD_ISS_ACMD41   : std_logic_vector(sdstate_type'range) := "0100";
--   constant SD_CHECK_CMD16  : std_logic_vector(sdstate_type'range) := "0101";
--   constant SD_READY        : std_logic_vector(sdstate_type'range) := "0110";
--   constant SD_CHECK_CMD17  : std_logic_vector(sdstate_type'range) := "0111";
--   constant SD_CHECK_TOKEN  : std_logic_vector(sdstate_type'range) := "1000";
--   constant SD_HANDLE_DATA  : std_logic_vector(sdstate_type'range) := "1001";
--   constant SD_SEND_CMD     : std_logic_vector(sdstate_type'range) := "1010";
--   constant SD_GET_RESP     : std_logic_vector(sdstate_type'range) := "1011";
 
  -----------------------------------------------------------------------------
  -- Types
  -----------------------------------------------------------------------------  
 
  type spim_ctrl_reg_type is record     -- Control register
       eas  : std_ulogic;               -- Enable alternate scaler
       ien  : std_ulogic;               -- Interrupt enable
       usrc : std_ulogic;               -- User mode
  end record;
 
  type spim_stat_reg_type is record     -- Status register
      busy : std_ulogic;                -- Core busy
      done : std_ulogic;                -- User operation done
  end record;
 
  type spim_regif_type is record        -- Register bank
       ctrl : spim_ctrl_reg_type;       -- Control register
       stat : spim_stat_reg_type;       -- Status register
  end record;
 
  type sdcard_type is record            -- Present when SD card
      state   : sdstate_type;           -- SD state
      tcnt    : std_logic_vector(2 downto 0);  -- Transmit count
      rcnt    : std_logic_vector(3 downto 0);  -- Receive count
      cmd     : std_logic_vector(5 downto 0);  -- SD command
      rstate  : sdstate_type;           -- Return state
      htb     : std_ulogic; -- Handle trailing byte
      vresp   : std_ulogic; -- Valid response
      cd      : std_ulogic; -- Synchronized card detect
      timeout : std_ulogic; -- Timeout status bit 
      dtocnt  : std_logic_vector(18 downto 0); -- Data token timeout counter
      ctocnt  : std_logic_vector(6 downto 0);  -- CMD resp. timeout counter
  end record;
 
  type spiflash_type is record          -- Present when !SD card
       state : spistate_type;           -- Mem. device comm. state
       cnt   : std_logic_vector(1 downto 0);  -- Generic counter
       hsize : std_logic_vector(1 downto 0);  -- Size of access
  end record;
 
  type spim_reg_type is record
       -- Common
       spimstate      : spimstate_type;  -- Main FSM
       rst            : std_ulogic;      -- Reset
       reg            : spim_regif_type; -- Register bank
       timer          : std_logic_vector((timer_size-1) downto 0);
       sample         : std_ulogic;     -- Sample data line
       sreg           : std_logic_vector(7 downto 0);  -- Shiftreg
       bcnt           : std_logic_vector(2 downto 0);  -- Bit counter
       go             : std_ulogic;     -- SPI comm. active
       stop           : std_ulogic;     -- Stop SPI comm.
       ar             : std_logic_vector(31 downto 0); -- argument/response
       hold           : std_ulogic;     -- Do not shift ar
       insplit        : std_ulogic;     -- SPLIT response issued
       unsplit        : std_ulogic;     -- SPLIT complete not issued
       -- SPI flash device
       spi            : spiflash_type;  -- Used when !SD card
       -- SD
       sd             : sdcard_type;    -- Used when SD card
       -- AHB
       irq            : std_ulogic;     -- Interrupt request
       hsize          : std_logic_vector(1 downto 0);
       hwrite         : std_ulogic;
       hsel           : std_ulogic;
       hmbsel         : std_logic_vector(0 to 1);
       haddr          : std_logic_vector((req_addr_bits-1) downto 0);
       hready         : std_ulogic;
       frdata         : std_logic_vector(31 downto 0);  -- Flash response data
       rrdata         : std_logic_vector(7 downto 0);  -- Register response data
       hresp          : std_logic_vector(1 downto 0);
       splmst         : std_logic_vector(3 downto 0);  -- SPLIT:ed master
       hsplit         : std_logic_vector(15 downto 0);  -- Other SPLIT:ed masters
       ahbcancel      : std_ulogic;     -- Locked access cancels ongoing SPLIT
                                        -- response
       -- Inputs and outputs
       spii           : spimctrl_in_type;
       spio           : spimctrl_out_type;
  end record;
 
  -----------------------------------------------------------------------------
  -- Signals
  -----------------------------------------------------------------------------
 
  signal r, rin : spim_reg_type;
 
begin  -- rtl
 
  comb: process (r, rstn, ahbsi, spii)
    variable v                : spim_reg_type;
    variable change           : std_ulogic;
    variable regaddr          : std_logic_vector(7 downto 2);
    variable hsplit           : std_logic_vector(15 downto 0);
    variable ahbirq           : std_logic_vector((NAHBIRQ-1) downto 0);
    variable lastbit          : std_ulogic;
    variable bytedone         : std_ulogic;
    variable enable_altscaler : boolean;
    variable disable_flash    : boolean;
    variable read_flash       : boolean;
  begin  -- process comb
    v := r; v.spii := spii; v.sample := '0'; change := '0';
    v.irq := '0'; v.hresp := HRESP_OKAY; v.hready := '1';
    regaddr := r.haddr(7 downto 2); hsplit := (others => '0');
    ahbirq := (others => '0'); ahbirq(hirq) := r.irq;
    if sdcard = 1 then v.sd.cd := r.spii.cd; else v.sd.cd := '0'; end if;
    read_flash := false;
    enable_altscaler := (not r.spio.initialized or r.reg.ctrl.eas) = '1';
    disable_flash := (r.spio.errorn = '0' or r.reg.ctrl.usrc = '1' or
                      r.spio.initialized = '0' or r.spimstate = USER_SPI);
    if dualoutput = 1 and sdcard = 0 then
      lastbit := andv(r.bcnt(1 downto 0)) and
                 ((r.spio.mosioen xnor INPUT) or r.bcnt(2)); 
    else
      lastbit := andv(r.bcnt);
    end if;
    bytedone := lastbit and r.sample;
 
    ---------------------------------------------------------------------------
    -- AHB communication
    ---------------------------------------------------------------------------
    if ahbsi.hready = '1' then
      if (ahbsi.hsel(hindex) and ahbsi.htrans(1)) = '1' then
        v.hmbsel := ahbsi.hmbsel(r.hmbsel'range);
        if (spliten = 0 or r.spimstate /= AHB_RESPOND or
            ahbsi.hmbsel(CTRL_BANK) = '1' or ahbsi.hmastlock = '1') then
          -- Writes to register space have no wait state
          v.hready := ahbsi.hmbsel(CTRL_BANK) and ahbsi.hwrite;            
          v.hsize := ahbsi.hsize(1 downto 0);
          v.hwrite := ahbsi.hwrite;
          v.haddr := ahbsi.haddr(r.haddr'range);
          v.hsel := '1';
          if ahbsi.hmbsel(FLASH_BANK) = '1' then
            if ahbsi.hwrite = '1' or disable_flash then
              v.hresp := HRESP_ERROR;
              v.hsel := '0';
            else
              if spliten /= 0 then
                if ahbsi.hmastlock = '0' then
                  v.hresp := HRESP_SPLIT;
                  v.splmst := ahbsi.hmaster;
                  v.unsplit := '1';
                else
                  v.ahbcancel := r.insplit;
                end if;
                v.insplit := not ahbsi.hmastlock;
              end if;
            end if;
          end if;
        else
          -- Core is busy, transfer is not locked and access was to flash
          -- area. Respond with SPLIT or insert wait states
          v.hready := '0';
          if spliten /= 0 then
            v.hresp := HRESP_SPLIT;
            v.hsplit(conv_integer(ahbsi.hmaster)) := '1';
          end if;
        end if;
      else
        v.hsel := '0';
      end if;
    end if;
 
    if (r.hready = '0') then
      if (r.hresp = HRESP_OKAY) then v.hready := '0';
      else v.hresp := r.hresp; end if;
    end if;    
 
    -- Read access to core registers
    if (r.hsel and r.hmbsel(CTRL_BANK) and not r.hwrite) = '1' then
      v.rrdata := (others => '0');
      v.hready := '1';
      v.hsel := '0';
      case regaddr is
        when CONF_REG_OFF =>
          if sdcard = 1 then
            v.rrdata := (others => '0');
          else
            v.rrdata := cslv(readcmd, 8);
          end if;
        when CTRL_REG_OFF =>
          v.rrdata(3) := r.spio.csn;
          v.rrdata(2) := r.reg.ctrl.eas;
          v.rrdata(1) := r.reg.ctrl.ien;
          v.rrdata(0) := r.reg.ctrl.usrc;
        when STAT_REG_OFF =>
          v.rrdata(5) := r.sd.cd;
          v.rrdata(4) := r.sd.timeout;
          v.rrdata(3) := not r.spio.errorn;
          v.rrdata(2) := r.spio.initialized;
          v.rrdata(1) := r.reg.stat.busy;
          v.rrdata(0) := r.reg.stat.done;
        when RX_REG_OFF => v.rrdata := r.ar(7 downto 0);
        when others => null;
      end case;
    end if;
 
    -- Write access to core registers
    if (r.hsel and r.hmbsel(CTRL_BANK) and r.hwrite) = '1' then
      case regaddr is
        when CTRL_REG_OFF =>
          v.rst           := ahbsi.hwdata(4);
          if (r.reg.ctrl.usrc and not ahbsi.hwdata(0)) = '1' then
            v.spio.csn := '1';
          elsif ahbsi.hwdata(0) = '1' then
            v.spio.csn := ahbsi.hwdata(3);
          end if;
          v.reg.ctrl.eas  := ahbsi.hwdata(2);
          v.reg.ctrl.ien  := ahbsi.hwdata(1);
          v.reg.ctrl.usrc := ahbsi.hwdata(0);
        when STAT_REG_OFF =>
          v.spio.errorn := r.spio.errorn or ahbsi.hwdata(3);
          v.reg.stat.done := r.reg.stat.done and not ahbsi.hwdata(0);
        when RX_REG_OFF => null;
        when TX_REG_OFF =>
          if r.reg.ctrl.usrc = '1' then
            v.sreg := ahbsi.hwdata(7 downto 0);
          end if;
        when others => null;
      end case;
    end if;
 
    ---------------------------------------------------------------------------
    -- SPIMCTRL control FSM
    ---------------------------------------------------------------------------
    v.reg.stat.busy := '1';
 
    case r.spimstate is
      when BUSY =>
        if r.spio.ready = '1' then
          v.spimstate := IDLE;
        end if;
 
      when AHB_RESPOND =>
        if r.spio.ready = '1' then
          if spliten /= 0 and r.unsplit = '1' then
            hsplit(conv_integer(r.splmst)) := '1';
            v.unsplit := '0';
          end if;
          if ((spliten = 0 or v.ahbcancel = '0') and
              (spliten = 0 or ahbsi.hmaster = r.splmst or r.insplit = '0') and
              ahbsi.hmbsel(FLASH_BANK) = '1' and
              (((ahbsi.hsel(hindex) and ahbsi.hready and ahbsi.htrans(1)) = '1') or
               ((spliten = 0 or r.insplit = '0') and r.hready = '0' and r.hresp = HRESP_OKAY))) then
            v.spimstate := IDLE;
            v.hresp := HRESP_OKAY;
            if spliten /= 0 then
              v.insplit := '0';
              v.hsplit := r.hsplit;
            end if;
            v.hready := '1';
            v.hsel := '0';
            if r.spio.errorn = '0' then
              v.hready := '0';
              v.hresp := HRESP_ERROR;
            end if;
          elsif spliten /= 0 and v.ahbcancel = '1' then
            v.spimstate := IDLE;
            v.ahbcancel := '0';
          end if;
        end if; 
 
      when USER_SPI =>
        if bytedone = '1' then
          v.spimstate := IDLE;
          v.reg.stat.done:= '1';
          v.irq := r.reg.ctrl.ien;
          v.hold := '1';
        end if;
 
      when others => -- IDLE
        if spliten /= 0 and r.hresp /= HRESP_SPLIT then
          hsplit := r.hsplit;
          v.hsplit := (others => '0');
        end if;
        v.reg.stat.busy := '0';
        if r.hsel = '1' then
          if r.hmbsel(FLASH_BANK) = '1' then
            -- Access to memory mapped flash area
            v.spimstate := AHB_RESPOND;
            read_flash := true;
          elsif regaddr = TX_REG_OFF and (r.hwrite and r.reg.ctrl.usrc) = '1' then
            -- Access to core transmit register
            v.spimstate := USER_SPI;
            v.go := '1';
            v.stop := '1';
            change := '1';
            v.hold := '0';
          end if;  
        end if;
    end case;
 
    ---------------------------------------------------------------------------
    -- SD Card specific code
    ---------------------------------------------------------------------------
    -- SD card initialization sequence:
    -- * Check if card is present
    -- * Perform power-up initialization sequence
    -- * Issue CMD0   GO_IDLE_STATE
    -- * Issue CMD55  APP_CMD
    -- * Issue ACMD41 SEND_OP_COND
    -- * Issue CMD16  SET_BLOCKLEN
    if sdcard = 1 then
      case r.sd.state is  
        when SD_PWRUP0 =>
          v.go := '1';
          v.sd.vresp := '1';
          v.sd.state := SD_GET_RESP;
          v.sd.rstate := SD_PWRUP1;
          v.sd.rcnt := cslv(2, r.sd.rcnt'length);
 
        when SD_PWRUP1 =>
          v.sd.state := SD_SEND_CMD;
          v.sd.rstate := SD_INIT_IDLE;
          v.sd.cmd := SD_CMD0;
          v.sd.rcnt := (others => '0');
          v.ar := (others => '0');
 
        when SD_INIT_IDLE => 
          v.sd.state := SD_SEND_CMD;
          v.sd.rcnt := (others => '0');
          if r.ar(0) = '0' and r.sd.cmd /= SD_CMD0 then
            v.sd.cmd := SD_CMD16;
            v.ar := SD_BLOCKLEN;
            v.sd.rstate := SD_CHECK_CMD16;
          else
            v.sd.cmd := SD_CMD55;
            v.ar := (others => '0');
            v.sd.rstate := SD_ISS_ACMD41;
          end if;
 
        when SD_ISS_ACMD41 =>
          v.sd.state := SD_SEND_CMD;
          v.sd.cmd := SD_ACMD41;
          v.sd.rcnt := (others => '0');
          v.ar := (others => '0');
          v.sd.rstate := SD_INIT_IDLE;
 
        when SD_CHECK_CMD16 => 
          if r.ar(7 downto 0) /= zero32(7 downto 0) then
            v.spio.errorn := '0';
          else
            v.spio.errorn := '1';
            v.spio.initialized := '1';
            v.sd.timeout := '0';
          end if;
          v.sd.state := SD_READY;
 
        when SD_READY => 
          v.spio.ready := '1';
          v.sd.cmd := SD_CMD17;
          v.sd.rstate := SD_CHECK_CMD17;
          if read_flash then
            v.sd.state := SD_SEND_CMD;
            v.spio.ready := '0';
            v.ar := (others => '0');
            v.ar(r.haddr'left downto 2) := r.haddr(r.haddr'left downto 2);
          end if;
 
        when SD_CHECK_CMD17 =>
          if r.ar(7 downto 0) /= X"00" then
            v.sd.state := SD_READY;
            v.spio.errorn := '0';
          else
            v.sd.rstate := SD_CHECK_TOKEN;
            v.spio.csn := '0';
            v.go := '1';
            change := '1';
          end if;
          v.sd.dtocnt := cslv(SD_DATATOK_TIMEOUT, r.sd.dtocnt'length);
          v.sd.state := SD_GET_RESP;
          v.sd.vresp := '1';
          v.hold := '0';
 
        when SD_CHECK_TOKEN =>
          if (r.ar(7 downto 5) = "111" and
              r.sd.dtocnt /= zero32(r.sd.dtocnt'range)) then
            v.sd.dtocnt := r.sd.dtocnt - 1;
            v.sd.state := SD_GET_RESP;
            if r.ar(0) = '0' then
              v.sd.rstate := SD_HANDLE_DATA;
              v.sd.rcnt := cslv(SD_BLEN-1, r.sd.rcnt'length);
            end if;
            v.spio.csn := '0';
            v.go := '1';
            change := '1';
          else
            v.spio.errorn := '0';
            v.sd.state := SD_READY;
          end if;
          v.sd.timeout := not orv(r.sd.dtocnt);
          v.sd.ctocnt := cslv(SD_CMD_TIMEOUT, r.sd.ctocnt'length);
          v.hold := '0';
 
        when SD_HANDLE_DATA =>
          v.frdata := r.ar;
          -- Receive and discard CRC
          v.sd.state := SD_GET_RESP;
          v.sd.rstate := SD_READY;
          v.sd.htb := '1';
          v.spio.csn := '0';
          v.go := '1';
          change := '1';
          v.sd.vresp := '1';
          v.spio.errorn := '1';
 
        when SD_SEND_CMD =>
          v.sd.htb := '1';
          v.sd.vresp := '0';
          v.spio.csn := '0';
          v.sd.ctocnt := cslv(SD_CMD_TIMEOUT, r.sd.ctocnt'length);
          if (bytedone or not r.go) = '1'then
            v.hold := '0';
            case r.sd.tcnt is
              when "000" => v.sreg := "01" & r.sd.cmd;
                            v.hold := '1'; change := '1';
              when "001" => v.sreg := r.ar(31 downto 24);
              when "010" => v.sreg := r.ar(30 downto 23);
              when "011" => v.sreg := r.ar(30 downto 23);
              when "100" => v.sreg := r.ar(30 downto 23);
              when "101" => v.sreg := SD_CRC_BYTE;
              when others => v.sd.state := SD_GET_RESP;
            end case;
            v.go := '1';
            v.sd.tcnt := r.sd.tcnt + 1;
          end if;
 
        when SD_GET_RESP =>
          if bytedone = '1' then
            if r.sd.vresp = '1' or r.sd.ctocnt = zero32(r.sd.ctocnt'range) then
              if r.sd.rcnt = zero32(r.sd.rcnt'range) then
                if r.sd.htb = '0' then
                  v.spio.csn := '1';
                end if;
                v.sd.htb := '0';
                v.hold := '1';
              else
                v.sd.rcnt := r.sd.rcnt - 1;
              end if;
            else
              v.sd.ctocnt := r.sd.ctocnt - 1;
            end if;
          end if;
          if lastbit = '1' then
            v.sd.vresp := r.sd.vresp or not r.ar(6);
            if r.sd.rcnt = zero32(r.sd.rcnt'range) then
              v.stop := r.sd.vresp and r.go and not r.sd.htb;
            end if;
          end if;
          if r.sd.ctocnt = zero32(r.sd.ctocnt'range) then
            v.stop := r.go;
          end if;
          if (r.go or r.spio.sck) = '0' then
            v.sd.state := r.sd.rstate;
            if r.sd.ctocnt = zero32(r.sd.ctocnt'range) then
              if r.spio.initialized = '1' then
                v.sd.state := SD_READY;
              else
                -- Try to initialize again
                v.sd.state := SD_CHECK_PRES;
              end if;
              v.spio.errorn := '0';
              v.sd.timeout := '1';
            end if;
            v.spio.csn := '1';
          end if;
          v.sd.tcnt := (others => '0');
 
        when others => -- SD_CHECK_PRES
          if r.sd.cd = '1' then
            v.go := '1';
            v.spio.csn := '0';
            v.sd.state := SD_GET_RESP;
            v.spio.cdcsnoen := OUTPUT;
          end if;
          v.sd.htb := '0';
          v.sd.vresp := '1';
          v.sd.rstate := SD_PWRUP0;
          v.sd.rcnt := cslv(10, r.sd.rcnt'length);
          v.sd.ctocnt := cslv(SD_CMD_TIMEOUT, r.sd.ctocnt'length);
      end case; 
    end if;
 
    ---------------------------------------------------------------------------
    -- SPI Flash (non SD) specific code
    ---------------------------------------------------------------------------
    if sdcard = 0 then
      case r.spi.state is  
        when SPI_READ =>
          if r.go = '0' then
            v.go := '1';
            change := '1';
          end if;
          v.hold := '1';  
          v.spi.cnt := cslv(SPI_ARG_LEN, r.spi.cnt'length);
          if bytedone = '1' then
            v.spi.state := SPI_ADDR;
            v.sreg := r.ar(23 downto 16);
            v.hold := '0';
          end if;
 
        when SPI_ADDR =>
          if bytedone = '1' then
            v.hold := '0';
            v.sreg := r.ar(22 downto 15);
            if r.spi.cnt = zero32(r.spi.cnt'range) then
              v.spi.state := SPI_DATA;
              if dualoutput = 1 then
                v.spio.mosioen := INPUT;
              end if;
              if r.spi.hsize = SPI_HSIZE_WORD then
                v.spi.cnt := (others => '1');
              else
                v.spi.cnt := r.spi.hsize;
              end if;
            else
              v.spi.cnt := r.spi.cnt - 1;
            end if;
          end if;
 
        when SPI_DATA =>
          if bytedone = '1' then
            v.spi.cnt := r.spi.cnt - 1;
          end if;
          if lastbit = '1' and r.spi.cnt = zero32(r.spi.cnt'range) then
            v.stop := r.go;
          end if;
          if (r.go or r.spio.sck) = '0' then
            if dualoutput = 1 then
              v.spio.mosioen := OUTPUT;
            end if;
            v.spio.csn := '1';
            v.spi.state := SPI_READY;
            -- Need to clear MSB in bcnt here since dualoutput may leave it at
            -- '1' and thereby making the next command short. 
            if dualoutput = 1 then
              v.bcnt(2) := '0';
            end if;
          end if;
 
        when SPI_READY =>
          v.spio.ready := '1';
          if read_flash then
            v.spi.state := SPI_READ;
            v.ar := (others => '0');
            v.ar(r.haddr'range) := r.haddr;
            v.spio.ready := '0';
            v.spio.csn := '0';
            v.sreg := cslv(readcmd, 8);
          end if;
          if r.spio.ready = '0' then
            case r.spi.hsize is
              when SPI_HSIZE_BYTE =>
                v.frdata := (r.ar(7 downto 0) & r.ar(7 downto 0) &
                             r.ar(7 downto 0) & r.ar(7 downto 0));
              when SPI_HSIZE_HWORD =>
                v.frdata := r.ar(15 downto 0) & r.ar(15 downto 0);
              when others =>
                v.frdata := r.ar;
            end case;
          end if;
          v.spi.hsize := r.hsize;
 
        when others => -- SPI_PWRUP
          if pwrupcnt /= 0 then
            v.frdata := r.frdata - 1;
            if r.frdata = zero32 then
              v.spio.initialized := '1';
              v.spi.state := SPI_READY;
            end if;
          else
            v.spio.initialized := '1';
            v.spi.state := SPI_READY;
          end if;
      end case;
    end if;
 
    ---------------------------------------------------------------------------
    -- SPI communication
    ---------------------------------------------------------------------------
    -- Clock generation
    if (r.go or r.spio.sck) = '1' then
      v.timer := r.timer - 1;
      if sck_toggle(v.timer, r.timer, enable_altscaler) then
        v.spio.sck := not r.spio.sck;
        v.sample := not r.spio.sck;
        change := r.spio.sck and r.go;
        if (v.stop and lastbit and not r.spio.sck) = '1' then
          v.go := '0';
          v.stop := '0';
        end if;
      end if;
    else
      v.timer := (others => '1');
    end if;
 
    if r.sample = '1' then
      if r.hold = '0' then
        if dualoutput = 1 and r.spio.mosioen = INPUT then
          v.ar := r.ar(29 downto 0) & r.spii.miso & r.spii.mosi;
        else
          v.ar := r.ar(30 downto 0) & r.spii.miso;
        end if;
      end if;
      v.bcnt := r.bcnt + 1;
    end if;
 
    if change = '1' then
      v.spio.mosi := v.sreg(7);
      v.sreg(7 downto 0) := v.sreg(6 downto 0) & '1';
    end if;
 
    ---------------------------------------------------------------------------
    -- System and core reset
    ---------------------------------------------------------------------------
    if (not rstn or r.rst) = '1' then
      if sdcard = 1 then
        v.sd.state := SD_CHECK_PRES;
        v.spio.cdcsnoen := INPUT;
        v.sd.timeout := '0';
      else
        v.spi.state := SPI_PWRUP;
        v.frdata := cslv(pwrupcnt, r.frdata'length);
        v.spio.cdcsnoen := OUTPUT;
      end if;
      v.spimstate        := IDLE;
      v.rst              := '0';
      --
      v.reg.ctrl         := ('0', '0', '0');
      v.reg.stat.done    := '0';
      --
      v.sample           := '0';
      v.sreg             := (others => '1');
      v.bcnt             := (others => '0');
      v.go               := '0';
      v.stop             := '0';
      v.hold             := '0';
      --
      v.hready           := '1';
      v.hwrite           := '0';
      v.hsel             := '0';
      v.hmbsel           := (others => '0');
      v.ahbcancel        := '0';
      --
      v.spio.sck         := '0';
      v.spio.mosi        := '1';
      v.spio.mosioen     := OUTPUT;
      v.spio.csn         := '1';
      v.spio.errorn      := '1';
      v.spio.initialized := '0';
      v.spio.ready       := '0';      
    end if;
 
    ---------------------------------------------------------------------------
    -- Drive unused signals
    ---------------------------------------------------------------------------
    if sdcard = 1 then
      v.spi.state  := SPI_PWRUP;
      v.spi.cnt    := (others => '0');
      v.spi.hsize  := (others => '0');
    else
      v.sd.state   := SD_CHECK_PRES;
      v.sd.tcnt    := (others => '0');
      v.sd.rcnt    := (others => '0');
      v.sd.cmd     := (others => '0');
      v.sd.rstate  := SD_CHECK_PRES;
      v.sd.htb     := '0';
      v.sd.vresp   := '0';
      v.sd.timeout := '0';
      v.sd.dtocnt  := (others => '0');
      v.sd.ctocnt  := (others => '0');
    end if;
    if spliten = 0 then
      v.insplit   := '0';
      v.unsplit   := '0';
      v.splmst    := (others => '0');
      v.hsplit    := (others => '0');
      v.ahbcancel := '0';
    end if;
 
    ---------------------------------------------------------------------------
    -- Signal assignments
    ---------------------------------------------------------------------------    
    -- Core registers
    rin <= v;
 
    -- AHB slave output
    ahbso.hready  <= r.hready;
    ahbso.hresp   <= r.hresp;
    if r.hmbsel(CTRL_BANK) = '1' then
      ahbso.hrdata <= zero32(31 downto 8) & r.rrdata;
    else
      ahbso.hrdata <= r.frdata;
    end if;
    ahbso.hconfig <= HCONFIG;
    ahbso.hcache  <= '0';
    ahbso.hirq    <= ahbirq;
    ahbso.hindex  <= hindex;
    ahbso.hsplit  <= hsplit;
 
    -- SPI signals
    spio <= r.spio;
  end process comb;
 
  reg: process (clk)
  begin  -- process reg
    if rising_edge(clk) then
      r <= rin;
    end if;
  end process reg;
 
  -- Boot message
  -- pragma translate_off
  bootmsg : report_version 
    generic map (
      "spimctrl" & tost(hindex) & ": SPI memory controller rev " &
      tost(REVISION) & ", irq " & tost(hirq));
  -- pragma translate_on
 
end rtl;
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.