------------------------------------------------------------------------------ -- This file is a part of the GRLIB VHDL IP LIBRARY -- Copyright (C) 2003 - 2008, Gaisler Research -- Copyright (C) 2008 - 2014, Aeroflex Gaisler -- Copyright (C) 2015 - 2017, Cobham Gaisler -- -- 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: spictrlx -- File: spictrlx.vhd -- Author: Jan Andersson - Aeroflex Gaisler AB -- Auto mode: J. Andersson, J. Ekergarn - Aeroflex Gaisler AB -- Contact: support@gaisler.com -- -- Description: SPI controller with an interface compatible with MPC83xx SPI. -- Relies on APB's wait state between back-to-back transfers. -- ------------------------------------------------------------------------------- library ieee; use ieee.numeric_std.all; use ieee.std_logic_1164.all; library techmap; use techmap.gencomp.all; library grlib; use grlib.config_types.all; use grlib.config.all; use grlib.stdlib.all; library gaisler; use gaisler.spi.all; entity spictrlx is generic ( rev : integer := 0; -- Core revision fdepth : integer range 1 to 7 := 1; -- FIFO depth is 2^fdepth slvselen : integer range 0 to 1 := 0; -- Slave select register enable slvselsz : integer range 1 to 32 := 1; -- Number of slave select signals oepol : integer range 0 to 1 := 0; -- Output enable polarity odmode : integer range 0 to 1 := 0; -- Support open drain mode, only -- set if pads are i/o or od pads. automode : integer range 0 to 1 := 0; -- Enable automated transfer mode acntbits : integer range 1 to 32 := 32; -- # Bits in am period counter aslvsel : integer range 0 to 1 := 0; -- Automatic slave select twen : integer range 0 to 1 := 1; -- Enable three wire mode maxwlen : integer range 0 to 15 := 0; -- Maximum word length; syncram : integer range 0 to 1 := 1; -- Use SYNCRAM for buffers memtech : integer range 0 to NTECH := 0; -- Memory technology ft : integer range 0 to 2 := 0; -- Fault-Tolerance scantest : integer range 0 to 1 := 0; -- Scan test support syncrst : integer range 0 to 1 := 0; -- Use only sync reset automask0 : integer := 0; -- Mask 0 for automated transfers automask1 : integer := 0; -- Mask 1 for automated transfers automask2 : integer := 0; -- Mask 2 for automated transfers automask3 : integer := 0; -- Mask 3 for automated transfers ignore : integer range 0 to 1 := 0; -- Ignore samples; prot : integer range 0 to 2 := 0 -- 0: Legacy, 1: dual, 2: quad ); port ( rstn : in std_ulogic; clk : in std_ulogic; -- APB signals apbi_psel : in std_ulogic; apbi_penable : in std_ulogic; apbi_paddr : in std_logic_vector(31 downto 0); apbi_pwrite : in std_ulogic; apbi_pwdata : in std_logic_vector(31 downto 0); apbi_testen : in std_ulogic; apbi_testrst : in std_ulogic; apbi_scanen : in std_ulogic; apbi_testoen : in std_ulogic; apbo_prdata : out std_logic_vector(31 downto 0); apbo_pirq : out std_ulogic; -- SPI signals spii_miso : in std_ulogic; spii_mosi : in std_ulogic; spii_sck : in std_ulogic; spii_spisel : in std_ulogic; spii_astart : in std_ulogic; spii_cstart : in std_ulogic; spii_ignore : in std_ulogic; spii_io2 : in std_ulogic; spii_io3 : in std_ulogic; spio_miso : out std_ulogic; spio_misooen : out std_ulogic; spio_mosi : out std_ulogic; spio_mosioen : out std_ulogic; spio_sck : out std_ulogic; spio_sckoen : out std_ulogic; spio_enable : out std_ulogic; spio_astart : out std_ulogic; spio_aready : out std_ulogic; spio_io2 : out std_ulogic; spio_io2oen : out std_ulogic; spio_io3 : out std_ulogic; spio_io3oen : out std_ulogic; slvsel : out std_logic_vector((slvselsz-1) downto 0) ); attribute sync_set_reset of rstn : signal is "true"; end entity spictrlx; architecture rtl of spictrlx is ----------------------------------------------------------------------------- -- Constants ----------------------------------------------------------------------------- constant OEPOL_LEVEL : std_ulogic := conv_std_logic(oepol = 1); constant OUTPUT : std_ulogic := OEPOL_LEVEL; -- Enable outputs constant INPUT : std_ulogic := not OEPOL_LEVEL; -- Tri-state outputs constant FIFO_DEPTH : integer := 2**fdepth; constant SLVSEL_EN : integer := slvselen; constant SLVSEL_SZ : integer := slvselsz; constant ASEL_EN : integer := aslvsel * slvselen; constant AM_EN : integer := automode; constant AM_CNT_BITS : integer := acntbits; constant OD_EN : integer := odmode; constant TW_EN : integer := twen; constant MAX_WLEN : integer := maxwlen; constant AM_MSK1_EN : boolean := AM_EN = 1 and FIFO_DEPTH > 32; constant AM_MSK2_EN : boolean := AM_EN = 1 and FIFO_DEPTH > 64; constant AM_MSK3_EN : boolean := AM_EN = 1 and FIFO_DEPTH > 96; constant FIFO_BITS : integer := fdepth; constant APBBITS : integer := 6+3*AM_EN; constant APBH : integer := 2+APBBITS-1; constant CAP0_ADDR : std_logic_vector(APBH downto 2) := conv_std_logic_vector(0, APBBITS); constant CAP1_ADDR : std_logic_vector(APBH downto 2) := conv_std_logic_vector(1, APBBITS); constant MODE_ADDR : std_logic_vector(APBH downto 2) := conv_std_logic_vector(8, APBBITS); constant EVENT_ADDR : std_logic_vector(APBH downto 2) := conv_std_logic_vector(9, APBBITS); constant MASK_ADDR : std_logic_vector(APBH downto 2) := conv_std_logic_vector(10, APBBITS); constant COM_ADDR : std_logic_vector(APBH downto 2) := conv_std_logic_vector(11, APBBITS); constant TD_ADDR : std_logic_vector(APBH downto 2) := conv_std_logic_vector(12, APBBITS); constant RD_ADDR : std_logic_vector(APBH downto 2) := conv_std_logic_vector(13, APBBITS); constant SLVSEL_ADDR : std_logic_vector(APBH downto 2) := conv_std_logic_vector(14, APBBITS); constant ASEL_ADDR : std_logic_vector(APBH downto 2) := conv_std_logic_vector(15, APBBITS); constant AMCFG_ADDR : std_logic_vector(APBH downto 2) := conv_std_logic_vector(16, APBBITS); constant AMPER_ADDR : std_logic_vector(APBH downto 2) := conv_std_logic_vector(17, APBBITS); constant AMMSK0_ADDR : std_logic_vector(10 downto 2) := "000010100"; -- 0x050 constant AMMSK1_ADDR : std_logic_vector(10 downto 2) := "000010101"; -- 0x054 constant AMMSK2_ADDR : std_logic_vector(10 downto 2) := "000010110"; -- 0x058 constant AMMSK3_ADDR : std_logic_vector(10 downto 2) := "000010111"; -- 0x05C constant AMTX_ADDR : std_logic_vector(10 downto 2) := "010000000"; -- 0x200 constant AMRX_ADDR : std_logic_vector(10 downto 2) := "100000000"; -- 0x40 constant SPICTRLCAPREG0 : std_logic_vector(31 downto 0) := conv_std_logic_vector(SLVSEL_SZ, 8) & conv_std_logic_vector(MAX_WLEN, 4) & conv_std_logic_vector(TW_EN, 1) & conv_std_logic_vector(AM_EN, 1) & conv_std_logic_vector(ASEL_EN, 1) & conv_std_logic_vector(SLVSEL_EN, 1) & conv_std_logic_vector(FIFO_DEPTH, 8) & conv_std_logic(syncram = 1) & conv_std_logic_vector(ft, 2) & conv_std_logic_vector(rev, 5); constant SPICTRLCAPREG1 : std_logic_vector(31 downto 0) := conv_std_logic_vector(prot, 32); -- Returns an integer containing the maximum characted length - 1 as -- restricted by the maxwlen VHDL generic. function wlen return integer is begin if MAX_WLEN = 0 then return 31; end if; return MAX_WLEN; end wlen; -- Returns needed number of bits for spi protocol tracking function spip_bits return integer is begin return 0; -- future extension --if prot = 2 then return 3; --elsif prot = 1 then return 2; --end if; --return 1; end spip_bits; constant PROG_AM_MASK : boolean := AM_EN = 1 and automask0 = 0 and (automask1 = 0 or FIFO_DEPTH <= 32) and (automask2 = 0 or FIFO_DEPTH <= 64) and (automask3 = 0 or FIFO_DEPTH <= 96); constant AM_MASK : std_logic_vector(127 downto 0) := conv_std_logic_vector_signed(automask3,32) & conv_std_logic_vector_signed(automask2,32) & conv_std_logic_vector_signed(automask1,32) & conv_std_logic_vector_signed(automask0,32); function check_discont_am_mask return boolean is variable foundzero : boolean; begin if AM_EN = 0 then return false; elsif PROG_AM_MASK then return true; else foundzero := false; for i in 0 to FIFO_DEPTH-1 loop if AM_MASK(i) = '0' then foundzero := true; else if foundzero then return true; end if; end if; end loop; return false; end if; end function; constant DISCONT_AM_MASK : boolean := check_discont_am_mask; function check_am_mask_end return integer is variable ret : integer; begin ret := 0; for i in 0 to FIFO_DEPTH-1 loop if AM_MASK(i) = '1' then ret := i; end if; end loop; return ret; end function; constant AM_MASK_END : integer := check_am_mask_end; ----------------------------------------------------------------------------- -- Types ----------------------------------------------------------------------------- type spi_mode_rec is record -- SPI Mode register amen : std_ulogic; loopb : std_ulogic; -- loopback mode cpol : std_ulogic; -- clock polarity cpha : std_ulogic; -- clock phase div16 : std_ulogic; -- Divide by 16 rev : std_ulogic; -- Reverse data mode ms : std_ulogic; -- Master/slave en : std_ulogic; -- Enable SPI len : std_logic_vector(3 downto 0); -- Bits per character pm : std_logic_vector(3 downto 0); -- Prescale modulus tw : std_ulogic; -- 3-wire mode asel : std_ulogic; -- Automatic slave select fact : std_ulogic; -- PM multiplication factor od : std_ulogic; -- Open drain mode cg : std_logic_vector(4 downto 0); -- Clock gap aseldel : std_logic_vector(1 downto 0); -- Asel delay tac : std_ulogic; tto : std_ulogic; -- Three-wire mode word order igsel : std_ulogic; -- Ignore spisel input cite : std_ulogic; -- Require SCK = CPOL for TIP end end record; type spi_em_rec is record -- SPI Event and Mask registers tip : std_ulogic; -- Transfer in progress/Clock generated lt : std_ulogic; -- last character transmitted ov : std_ulogic; -- slave/master overrun un : std_ulogic; -- slave/master underrun mme : std_ulogic; -- Multiple-master error ne : std_ulogic; -- Not empty nf : std_ulogic; -- Not full at : std_ulogic; -- Automated transfer end record; type spi_cmd_rec is record -- SPI command register lst : std_ulogic; sprot : std_logic_vector(2 downto 0); end record; type spi_fifo is array (0 to (1-syncram)*(FIFO_DEPTH-1)) of std_logic_vector(wlen downto 0); type spi_txfifo is array (0 to (1-syncram)*(FIFO_DEPTH-1)) of std_logic_vector(spip_bits+wlen downto 0); type spi_amcfg_rec is record -- AM config register seq : std_ulogic; -- Data must always be read out of receive queue strict : std_ulogic; -- Strict period ovtb : std_ulogic; -- Perform transfer on OV ovdb : std_ulogic; -- Skip data on OV act : std_ulogic; -- Start immediately eact : std_ulogic; -- Activate on external event erpt : std_ulogic; -- Repeat on external event, not on period done lock : std_ulogic; -- Lock receive registers when reading data ecgc : std_ulogic; -- External clock gap control end record; type spi_am_rec is record -- Automode state -- Register interface cfg : spi_amcfg_rec; -- AM config register per : std_logic_vector((AM_CNT_BITS-1)*AM_EN downto 0); -- AM period -- active : std_ulogic; -- Auto mode active lock : std_ulogic; cnt : unsigned((AM_CNT_BITS-1)*AM_EN downto 0); -- skipdata : std_ulogic; rxfull : std_ulogic; -- AM RX FIFO is filled rxfifo : spi_fifo; -- Receive data FIFO txfifo : spi_fifo; -- Transmit data FIFO rfreecnt : integer range 0 to FIFO_DEPTH; -- free rx fifo slots mask : std_logic_vector(FIFO_DEPTH-1 downto 0); mask_shdw : std_logic_vector(FIFO_DEPTH-1 downto 0); unread : std_logic_vector(FIFO_DEPTH-1 downto 0); at : std_ulogic; -- rxread : std_ulogic; txwrite : std_ulogic; txread : std_ulogic; apbaddr : std_logic_vector(FIFO_BITS-1 downto 0); rxsel : std_ulogic; end record; -- Two stage synchronizers on each input coming from off-chip type spi_in_local_type is record miso : std_ulogic; mosi : std_ulogic; sck : std_ulogic; spisel : std_ulogic; io2 : std_ulogic; io3 : std_ulogic; end record; type spi_in_array is array (1 downto 0) of spi_in_local_type; -- Local spi out type without ssn type spi_out_local_type is record miso : std_ulogic; misooen : std_ulogic; mosi : std_ulogic; mosioen : std_ulogic; sck : std_ulogic; sckoen : std_ulogic; enable : std_ulogic; astart : std_ulogic; aready : std_ulogic; io2 : std_ulogic; io2oen : std_ulogic; io3 : std_ulogic; io3oen : std_ulogic; end record; -- Yet another subset of out type to make it easier for certain tools to -- place registers near pads. type spi_out_local_lb_type is record mosi : std_ulogic; miso : std_ulogic; io2 : std_ulogic; io3 : std_ulogic; sck : std_ulogic; end record; type spi_reg_type is record -- SPI registers mode : spi_mode_rec; -- Mode register event : spi_em_rec; -- Event register mask : spi_em_rec; -- Mask register cmd : spi_cmd_rec; -- Command register td : std_logic_vector(31 downto 0); -- Transmit register rd : std_logic_vector(31 downto 0); -- Receive register slvsel : std_logic_vector((SLVSEL_SZ-1) downto 0); -- Slave select register aslvsel : std_logic_vector((SLVSEL_SZ-1) downto 0); -- Automatic slave select -- uf : std_ulogic; -- Slave in underflow condition ov : std_ulogic; -- Receive overflow condition td_occ : std_ulogic; -- Transmit register occupied rd_free : std_ulogic; -- Receive register free (empty) txfifo : spi_txfifo; -- Transmit data FIFO rxfifo : spi_fifo; -- Receive data FIFO rxd : std_logic_vector(wlen downto 0); -- Receive shift register txd : std_logic_vector(wlen downto 0); -- Transmit shift register -- txdprot : std_logic_vector(2 downto 0); -- Current tx mode -- rxdprot : std_logic_vector(1 downto 0); -- Current rx mode txdupd : std_ulogic; -- Update txd txdbyp : std_ulogic; -- txd update bypass toggle : std_ulogic; -- SCK has toggled samp : std_ulogic; -- Sample chng : std_ulogic; -- Change psck : std_ulogic; -- Previous value of SC twdir : std_ulogic; -- Direction in 3-wire mode syncsamp : std_logic_vector(1 downto 0); -- Sample synchronized input incrdli : std_ulogic; rxdone : std_ulogic; rxdone2 : std_ulogic; running : std_ulogic; ov2 : std_ulogic; -- counters tfreecnt : integer range 0 to FIFO_DEPTH; -- free td fifo slots rfreecnt : integer range 0 to FIFO_DEPTH; -- free td fifo slots tdfi : std_logic_vector(fdepth-1 downto 0); -- First tx queue element rdfi : std_logic_vector(fdepth-1 downto 0); -- First rx queue element tdli : std_logic_vector(fdepth-1 downto 0); -- Last tx queue element rdli : std_logic_vector(fdepth-1 downto 0); -- Last rx queue element rbitcnt : std_logic_vector(log2(wlen+1)-1 downto 0); -- Current receive bit tbitcnt : std_logic_vector(log2(wlen+1)-1 downto 0); -- Current transmit bit divcnt : unsigned(9 downto 0); -- Clock scaler cgcnt : unsigned(5 downto 0); -- Clock gap counter cgcntblock: std_ulogic; aselcnt : unsigned(1 downto 0); -- ASEL delay cgasel : std_ulogic; -- ASEL when entering CG -- irq : std_ulogic; -- -- Automode am : spi_am_rec; -- Sync registers for inputs spii : spi_in_array; -- Output spio : spi_out_local_type; spiolb : spi_out_local_lb_type; -- astart : std_ulogic; cstart : std_ulogic; txdupd2 : std_ulogic; twdir2 : std_ulogic; end record; ----------------------------------------------------------------------------- -- Sub programs ----------------------------------------------------------------------------- -- Returns a vector containing the character length - 1 in bits as selected -- by the Mode field LEN. function spilen ( len : std_logic_vector(3 downto 0)) return std_logic_vector is begin -- spilen if len = zero32(3 downto 0) then return "11111"; else return "0" & len; end if; end spilen; -- Write clear procedure wc ( reg_o : out std_ulogic; reg_i : in std_ulogic; b : in std_ulogic) is begin reg_o := reg_i and not b; end procedure wc; -- Reverses string. After this function has been called the first bit -- to send is always at position 0. function reverse( data : std_logic_vector) return std_logic_vector is variable rdata: std_logic_vector(data'reverse_range); begin for i in data'range loop rdata(i) := data(i); end loop; return rdata; end function reverse; -- Performs a HWORD swap if len /= 0 function condhwordswap ( data : std_logic_vector(31 downto 0); len : std_logic_vector(4 downto 0)) return std_logic_vector is variable rdata : std_logic_vector(31 downto 0); begin -- condhwordswap if len = one32(4 downto 0) then rdata := data; else rdata := data(15 downto 0) & data(31 downto 16); end if; return rdata; end condhwordswap; -- Zeroes out unused part of receive vector. function select_data ( data : std_logic_vector(wlen downto 0); len : std_logic_vector(4 downto 0)) return std_logic_vector is variable rdata : std_logic_vector(31 downto 0) := (others => '0'); variable length : integer range 0 to 31 := conv_integer(len); variable sdata : std_logic_vector(31 downto 0) := (others => '0'); begin -- select_data -- Quartus can not handle variable ranges -- rdata(conv_integer(len) downto 0) := data(conv_integer(len) downto 0); sdata := (others => '0'); sdata(wlen downto 0) := data; case length is when 15 => rdata(15 downto 0) := sdata(15 downto 0); when 14 => rdata(14 downto 0) := sdata(14 downto 0); when 13 => rdata(13 downto 0) := sdata(13 downto 0); when 12 => rdata(12 downto 0) := sdata(12 downto 0); when 11 => rdata(11 downto 0) := sdata(11 downto 0); when 10 => rdata(10 downto 0) := sdata(10 downto 0); when 9 => rdata(9 downto 0) := sdata(9 downto 0); when 8 => rdata(8 downto 0) := sdata(8 downto 0); when 7 => rdata(7 downto 0) := sdata(7 downto 0); when 6 => rdata(6 downto 0) := sdata(6 downto 0); when 5 => rdata(5 downto 0) := sdata(5 downto 0); when 4 => rdata(4 downto 0) := sdata(4 downto 0); when 3 => rdata(3 downto 0) := sdata(3 downto 0); when others => rdata := sdata; end case; return rdata; end select_data; -- purpose: Returns true when a slave is selected and the clock starts function slv_start ( spisel : std_ulogic; cpol : std_ulogic; sck : std_ulogic; fsck_chg : std_ulogic) return boolean is begin -- slv_start if spisel = '0' then -- Slave is selected if fsck_chg = '1' then -- The clock has changed return (cpol xor sck) = '1'; -- The clock is not idle end if; end if; return false; end slv_start; constant RESET_ALL : boolean := GRLIB_CONFIG_ARRAY(grlib_sync_reset_enable_all) = 1; constant ASYNC_RESET : boolean := GRLIB_CONFIG_ARRAY(grlib_async_reset_enable) = 1; function spictrl_resval return spi_reg_type is variable v : spi_reg_type; begin v.mode := ('0','0','0','0','0','0','0','0',"0000","0000", '0','0','0','0',"00000","00", '0', '0', '0', '0'); v.event := ('0', '0', '0', '0', '0', '0', '0', '0'); v.mask := ('0', '0', '0', '0', '0', '0', '0', '0'); v.cmd := ('0', (others => '0')); v.td := (others => '0'); v.rd := (others => '0'); v.slvsel := (others => '1'); v.aslvsel := (others => '0'); v.uf := '0'; v.ov := '0'; v.td_occ := '0'; v.rd_free := '1'; for i in 0 to (1-syncram)*(FIFO_DEPTH-1) loop v.txfifo(i) := (others => '0'); v.rxfifo(i) := (others => '0'); end loop; v.rxd := (others => '0'); v.txd := (others => '0'); v.txd(0) := '1'; -- v.txdprot := (others => '0'); -- v.rxdprot := (others => '0'); v.txdupd := '0'; v.txdbyp := '0'; v.toggle := '0'; v.samp := '1'; v.chng := '0'; v.psck := '0'; v.twdir := INPUT; v.syncsamp := (others => '0'); v.incrdli := '0'; v.rxdone := '0'; v.rxdone2 := '0'; v.running := '0'; v.ov2 := '0'; v.tfreecnt := FIFO_DEPTH; v.rfreecnt := FIFO_DEPTH; v.tdfi := (others => '0'); v.rdfi := (others => '0'); v.tdli := (others => '0'); v.rdli := (others => '0'); v.rbitcnt := (others => '0'); v.tbitcnt := (others => '0'); v.divcnt := (others => '0'); v.cgcnt := (others => '0'); v.cgcntblock := '0'; v.aselcnt := (others => '0'); v.cgasel := '0'; v.irq := '0'; v.am.cfg := ('0', '0', '0', '0', '0', '0', '0', '0', '0'); v.am.per := (others => '0'); v.am.active := '0'; v.am.lock := '0'; v.am.cnt := (others => '0'); v.am.skipdata := '0'; v.am.rxfull := '0'; for i in 0 to (1-syncram)*(FIFO_DEPTH-1) loop v.am.rxfifo := (others => (others => '0')); v.am.txfifo := (others => (others => '0')); end loop; v.am.rfreecnt := 0; v.am.mask := (others => '0'); v.am.mask_shdw := (others => '1'); v.am.unread := (others => '0'); v.am.at := '0'; v.am.rxread := '0'; v.am.txwrite := '0'; v.am.txread := '0'; v.am.apbaddr := (others => '0'); v.am.rxsel := '0'; for i in 1 downto 0 loop v.spii(i).miso := '1'; v.spii(i).mosi := '1'; v.spii(i).sck := '0'; v.spii(i).spisel := '1'; v.spii(i).io2 := '1'; v.spii(i).io3 := '1'; end loop; v.spio.miso := '1'; v.spio.misooen := INPUT; v.spio.mosi := '1'; v.spio.mosioen := INPUT; v.spio.sck := '0'; v.spio.sckoen := INPUT; v.spio.enable := '0'; v.spio.astart := '0'; v.spio.aready := '0'; v.spio.io2 := '0'; v.spio.io2oen := INPUT; v.spio.io3 := '0'; v.spio.io3oen := INPUT; v.spiolb.mosi := '1'; v.spiolb.miso := '1'; v.spiolb.io2 := '1'; v.spiolb.io3 := '1'; v.spiolb.sck := '1'; v.astart := '0'; v.cstart := '0'; v.txdupd2 := '0'; v.twdir2 := '0'; return v; end spictrl_resval; constant RES : spi_reg_type := spictrl_resval; ----------------------------------------------------------------------------- -- Signals ----------------------------------------------------------------------------- signal r, rin : spi_reg_type; type fifo_data_vector_array is array (automode downto 0) of std_logic_vector(wlen downto 0); type fifo_txdata_vector_array is array (automode downto 0) of std_logic_vector(spip_bits+wlen downto 0); type fifo_addr_vector_array is array (automode downto 0) of std_logic_vector(fdepth-1 downto 0); signal rx_di, rx_do : fifo_data_vector_array; signal tx_di, tx_do : fifo_txdata_vector_array; signal rx_ra, rx_wa, tx_ra, tx_wa : fifo_addr_vector_array; signal rx_read, tx_read, rx_write, tx_write : std_logic_vector(automode downto 0); signal arstn : std_ulogic; begin arstn <= apbi_testrst when (scantest = 1) and (apbi_testen = '1') else rstn; -- SPI controller, register interface and related logic comb: process (r, rstn, apbi_psel, apbi_penable, apbi_paddr, apbi_pwrite, apbi_pwdata, apbi_testen, apbi_testrst, apbi_scanen, apbi_testoen, spii_miso, spii_mosi, spii_sck, spii_spisel, spii_astart, rx_do, tx_do, spii_cstart, spii_ignore, spii_io2, spii_io3) variable v : spi_reg_type; variable apbaddr : std_logic_vector(APBH downto 2); variable apbout : std_logic_vector(31 downto 0); variable len : std_logic_vector(4 downto 0); variable indata : std_logic_vector(3 downto 0); variable change : std_ulogic; variable update : std_ulogic; variable sample : std_ulogic; variable reload : std_ulogic; variable cgasel : std_ulogic; variable txshift : std_ulogic; -- automode variable rstop1 : std_ulogic; variable rstop2 : std_ulogic; variable rstop3 : std_ulogic; variable tstop1 : std_ulogic; variable tstop2 : std_ulogic; variable tstop3 : std_ulogic; variable astart : std_ulogic; -- fifos variable rx_rd : std_ulogic; variable tx_rd : std_ulogic; variable rx_wr : std_ulogic; variable tx_wr : std_ulogic; -- variable fsck : std_ulogic; variable fsck_chg : std_ulogic; -- variable spisel : std_ulogic; -- variable rntxd : std_logic_vector(0 to 31); variable ntxd : std_logic_vector(wlen downto 0); variable ctxfifo : std_logic_vector(spip_bits+wlen downto 0); -- variable amask : std_logic_vector(FIFO_DEPTH-1 downto 0); variable aloop : integer; -- variable txdprot : std_logic_vector(1 downto 0); variable txdio : std_ulogic; variable rxdprot : std_logic_vector(1 downto 0); begin -- process comb v := r; v.irq := '0'; apbaddr := apbi_paddr(APBH downto 2); apbout := (others => '0'); len := spilen(r.mode.len); v.toggle := '0'; v.txdupd := '0'; v.syncsamp := r.syncsamp(0) & '0'; update := '0'; v.rxdone := '0'; indata := (others => '0'); sample := '0'; change := '0'; reload := '0'; v.spio.astart := '0'; cgasel := '0'; v.ov2 := r.ov; txshift := '0'; fsck := '0'; fsck_chg := '0'; v.txdbyp := '0'; spisel := r.spii(1).spisel or r.mode.igsel; ntxd := r.td(wlen downto 0); rntxd := reverse(r.td); if r.mode.rev = '1' then ntxd := rntxd(31-wlen to 31); end if; ctxfifo := (others => '0'); --rxdprot := r.rxdprot; --txdprot := r.txdprot(1 downto 0); txdio := r.txdprot(2); rxdprot := r.cmd.sprot(1 downto 0); txdprot := r.cmd.sprot(1 downto 0); txdio := r.cmd.sprot(2); v.spio.aready := '0'; if AM_EN = 1 then v.txdupd2 := '0'; v.cstart := '0'; if TW_EN = 1 then v.twdir2 := r.twdir; end if; end if; if PROG_AM_MASK then amask := r.am.mask; aloop := FIFO_DEPTH-1; else amask := AM_MASK(FIFO_DEPTH-1 downto 0); aloop := AM_MASK_END; end if; rx_rd := '0'; tx_rd := '0'; rx_wr := '0'; tx_wr := '0'; rstop1 := '0'; rstop2 := '0'; rstop3 := '0'; tstop1 := '0'; tstop2 := '0'; tstop3 := '0'; astart := '0'; v.am.txwrite := '0'; v.am.txwrite := '0'; v.am.rxread := '0'; if AM_EN = 1 then v.am.at := r.event.at; v.astart := spii_astart; if r.event.at = '0' then astart := spii_astart and (not r.astart); if PROG_AM_MASK then v.am.mask := r.am.mask_shdw; end if; end if; if spii_cstart = '1' then v.cstart := '1'; end if; end if; if (apbi_psel and apbi_penable and (not apbi_pwrite)) = '1' then if apbaddr = CAP0_ADDR then apbout := SPICTRLCAPREG0; elsif apbaddr = CAP1_ADDR then apbout := SPICTRLCAPREG1; elsif apbaddr = MODE_ADDR then apbout := r.mode.amen & r.mode.loopb & r.mode.cpol & r.mode.cpha & r.mode.div16 & r.mode.rev & r.mode.ms & r.mode.en & r.mode.len & r.mode.pm & r.mode.tw & r.mode.asel & r.mode.fact & r.mode.od & r.mode.cg & r.mode.aseldel & r.mode.tac & r.mode.tto & r.mode.igsel & r.mode.cite & zero32(0); elsif apbaddr = EVENT_ADDR then apbout := r.event.tip & zero32(30 downto 16) & r.event.at & r.event.lt & zero32(13) & r.event.ov & r.event.un & r.event.mme & r.event.ne & r.event.nf & zero32(7 downto 0); elsif apbaddr = MASK_ADDR then apbout := r.mask.tip & zero32(30 downto 16) & r.mask.at & r.mask.lt & zero32(13) & r.mask.ov & r.mask.un & r.mask.mme & r.mask.ne & r.mask.nf & zero32(7 downto 0); elsif apbaddr = COM_ADDR then -- LST always reads as zero if prot /= 0 then apbout(2 downto 0) := r.cmd.sprot; end if; elsif apbaddr = RD_ADDR then apbout := condhwordswap(r.rd, len); if AM_EN = 0 or r.mode.amen = '0' then v.rd_free := '1'; end if; elsif apbaddr = SLVSEL_ADDR then if SLVSEL_EN /= 0 then apbout((SLVSEL_SZ-1) downto 0) := r.slvsel; else null; end if; elsif apbaddr = ASEL_ADDR then if ASEL_EN /= 0 then apbout((SLVSEL_SZ-1) downto 0) := r.aslvsel; else null; end if; end if; end if; -- write registers if (apbi_psel and apbi_penable and apbi_pwrite) = '1' then if apbaddr = MODE_ADDR then if AM_EN = 1 then v.mode.amen := apbi_pwdata(31); end if; v.mode.loopb := apbi_pwdata(30); v.mode.cpol := apbi_pwdata(29); v.mode.cpha := apbi_pwdata(28); v.mode.div16 := apbi_pwdata(27); v.mode.rev := apbi_pwdata(26); v.mode.ms := apbi_pwdata(25); v.mode.en := apbi_pwdata(24); v.mode.len := apbi_pwdata(23 downto 20); v.mode.pm := apbi_pwdata(19 downto 16); if TW_EN = 1 then v.mode.tw := apbi_pwdata(15); end if; if ASEL_EN = 1 then v.mode.asel := apbi_pwdata(14); end if; v.mode.fact := apbi_pwdata(13); if OD_EN = 1 then v.mode.od := apbi_pwdata(12); end if; v.mode.cg := apbi_pwdata(11 downto 7); if ASEL_EN = 1 then v.mode.aseldel := apbi_pwdata(6 downto 5); v.mode.tac := apbi_pwdata(4); end if; if TW_EN = 1 then v.mode.tto := apbi_pwdata(3); end if; v.mode.igsel := apbi_pwdata(2); v.mode.cite := apbi_pwdata(1); elsif apbaddr = EVENT_ADDR then wc(v.event.lt, r.event.lt, apbi_pwdata(14)); wc(v.event.ov, r.event.ov, apbi_pwdata(12)); wc(v.event.un, r.event.un, apbi_pwdata(11)); wc(v.event.mme, r.event.mme, apbi_pwdata(10)); elsif apbaddr = MASK_ADDR then v.mask.tip := apbi_pwdata(31); if AM_EN = 1 then v.mask.at := apbi_pwdata(15); end if; v.mask.lt := apbi_pwdata(14); v.mask.ov := apbi_pwdata(12); v.mask.un := apbi_pwdata(11); v.mask.mme := apbi_pwdata(10); v.mask.ne := apbi_pwdata(9); v.mask.nf := apbi_pwdata(8); elsif apbaddr = COM_ADDR then if apbi_pwdata(22) = '1' then v.cmd.lst := '1'; end if; if prot /= 0 and apbi_pwdata(3) = '1' then v.cmd.sprot := apbi_pwdata(2 downto 0); end if; elsif apbaddr = TD_ADDR then -- The write is lost if the transmit register is written when -- the not full bit is zero. if r.event.nf = '1' then v.td := apbi_pwdata; if AM_EN = 0 or r.mode.amen = '0' then v.td_occ := '1'; end if; end if; elsif apbaddr = SLVSEL_ADDR then if SLVSEL_EN /= 0 then v.slvsel := apbi_pwdata((SLVSEL_SZ-1) downto 0); else null; end if; elsif apbaddr = ASEL_ADDR then if ASEL_EN /= 0 then v.aslvsel := apbi_pwdata((SLVSEL_SZ-1) downto 0); else null; end if; end if; end if; -- Automode register interface if AM_EN /= 0 then if apbi_psel = '1' then v.am.apbaddr := apbaddr(FIFO_BITS+1 downto 2); if syncram /= 0 then -- Check if tx queue will be read if apbaddr(10 downto 9) = AMTX_ADDR(10 downto 9) then v.am.txread := apbi_pwrite and not r.am.txread; end if; if apbaddr(10 downto 9) = AMRX_ADDR(10 downto 9) then v.am.rxread := not r.am.rxread; end if; end if; end if; if (apbi_psel and apbi_penable) = '1' then if apbaddr = AMCFG_ADDR then apbout := zero32(31 downto 9) & r.am.cfg.ecgc & r.am.cfg.lock & r.am.cfg.erpt & r.am.cfg.seq & r.am.cfg.strict & r.am.cfg.ovtb & r.am.cfg.ovdb & r.am.active & r.am.cfg.eact; if apbi_pwrite = '1' then v.am.cfg.ecgc := apbi_pwdata(8); v.am.cfg.lock := apbi_pwdata(7); v.am.cfg.erpt := apbi_pwdata(6); v.am.cfg.seq := apbi_pwdata(5); v.am.cfg.strict := apbi_pwdata(4); v.am.cfg.ovtb := apbi_pwdata(3); v.am.cfg.ovdb := apbi_pwdata(2); v.am.cfg.act := apbi_pwdata(1); v.spio.astart := apbi_pwdata(1); v.am.cfg.eact := apbi_pwdata(0); end if; elsif apbaddr = AMPER_ADDR then apbout((AM_CNT_BITS-1)*AM_EN downto 0) := r.am.per; if apbi_pwrite = '1' then v.am.per := apbi_pwdata((AM_CNT_BITS-1)*AM_EN downto 0); end if; elsif apbaddr = AMMSK0_ADDR then if FIFO_DEPTH > 32 then apbout := amask(31 downto 0); if PROG_AM_MASK then if apbi_pwrite = '1' then v.am.mask_shdw(31 downto 0) := apbi_pwdata; end if; end if; else apbout(FIFO_DEPTH-1 downto 0) := amask(FIFO_DEPTH-1 downto 0); if PROG_AM_MASK then if apbi_pwrite = '1' then v.am.mask_shdw(FIFO_DEPTH-1 downto 0) := apbi_pwdata(FIFO_DEPTH-1 downto 0); end if; end if; end if; elsif apbaddr = AMMSK1_ADDR then if AM_MSK1_EN then if FIFO_DEPTH > 64 then apbout := amask(63 downto 32); if PROG_AM_MASK then if apbi_pwrite = '1' then v.am.mask_shdw(63 downto 32) := apbi_pwdata; end if; end if; else apbout(FIFO_DEPTH-33 downto 0) := amask(FIFO_DEPTH-1 downto 32); if PROG_AM_MASK then if apbi_pwrite = '1' then v.am.mask_shdw(FIFO_DEPTH-1 downto 32) := apbi_pwdata(FIFO_DEPTH-33 downto 0); end if; end if; end if; else null; end if; elsif apbaddr = AMMSK2_ADDR then if AM_MSK2_EN then if FIFO_DEPTH > 96 then apbout := amask(95 downto 64); if PROG_AM_MASK then if apbi_pwrite = '1' then v.am.mask_shdw(95 downto 64) := apbi_pwdata; end if; end if; else apbout(FIFO_DEPTH-65 downto 0) := amask(FIFO_DEPTH-1 downto 64); if PROG_AM_MASK then if apbi_pwrite = '1' then v.am.mask_shdw(FIFO_DEPTH-1 downto 64) := apbi_pwdata(FIFO_DEPTH-65 downto 0); end if; end if; end if; else null; end if; elsif apbaddr = AMMSK3_ADDR then if AM_MSK3_EN then apbout(FIFO_DEPTH-97 downto 0) := amask(FIFO_DEPTH-1 downto 96); if PROG_AM_MASK then if apbi_pwrite = '1' then v.am.mask_shdw(FIFO_DEPTH-1 downto 96) := apbi_pwdata(FIFO_DEPTH-97 downto 0); end if; end if; else null; end if; elsif apbaddr(10 downto 9) = AMTX_ADDR(10 downto 9) then if conv_integer(apbaddr(8 downto 2)) < FIFO_DEPTH then if syncram = 0 then apbout(wlen downto 0) := r.am.txfifo(conv_integer(apbaddr(FIFO_BITS+1 downto 2))); else ctxfifo := tx_do(automode); apbout(wlen downto 0) := ctxfifo(wlen downto 0); end if; if apbi_pwrite = '1' then v.am.txwrite := '1'; v.td := apbi_pwdata; end if; end if; elsif apbaddr(10 downto 9) = AMRX_ADDR(10 downto 9) then if conv_integer(apbaddr(8 downto 2)) < FIFO_DEPTH then if syncram = 0 then if r.mode.rev = '0' then apbout := condhwordswap(reverse(select_data(r.rxfifo(conv_integer(r.am.apbaddr)), len)), len); else apbout := condhwordswap(select_data(r.rxfifo(conv_integer(r.am.apbaddr)), len), len); end if; else if r.mode.rev = '0' then apbout := condhwordswap(reverse(select_data(rx_do(conv_integer(not r.am.rxsel)), len)), len); else apbout := condhwordswap(select_data(rx_do(conv_integer(not r.am.rxsel)), len), len); end if; end if; if r.am.unread(conv_integer(r.am.apbaddr)) = '1' then v.rd_free := '1'; v.am.unread(conv_integer(r.am.apbaddr)) := '0'; v.am.lock := r.am.cfg.lock; end if; end if; end if; end if; end if; -- Handle transmit FIFO if r.td_occ = '1' and r.tfreecnt /= 0 then if syncram = 0 then v.txfifo(conv_integer(r.tdli)) := ntxd; else tx_wr := '1'; end if; v.tdli := r.tdli + 1; v.tfreecnt := r.tfreecnt - 1; v.td_occ := '0'; if r.tfreecnt = FIFO_DEPTH then v.txdbyp := r.running and r.mode.ms and r.txdupd; v.txdupd := not r.uf; tx_rd := '1'; end if; end if; -- AM transmit FIFO handling when core is not implemented with SYNCRAM if syncram = 0 and AM_EN /= 0 and r.am.txwrite = '1' then if r.mode.rev = '0' then v.am.txfifo(conv_integer(r.am.apbaddr)) := r.td(wlen downto 0); else v.am.txfifo(conv_integer(r.am.apbaddr)) := reverse(r.td)(31-wlen to 31); end if; end if; -- Update receive register and FIFO if r.rd_free = '1' and r.rfreecnt /= FIFO_DEPTH then if syncram = 0 then if r.mode.rev = '0' then v.rd := reverse(select_data(r.rxfifo(conv_integer(r.rdfi)), len)); else v.rd := select_data(r.rxfifo(conv_integer(r.rdfi)), len); end if; else if r.mode.rev = '0' then v.rd := reverse(select_data(rx_do(0), len)); else v.rd := select_data(rx_do(0), len); end if; end if; if not ((ignore > 0) and (spii_ignore = '1')) then v.rdfi := r.rdfi + 1; v.rfreecnt := r.rfreecnt + 1; v.rd_free := '0'; end if; end if; if v.rd_free = '1' and r.rfreecnt /= FIFO_DEPTH then rx_rd := '1'; end if; if r.mode.en = '1' then -- Core is enabled -- Not full detection if r.tfreecnt /= 0 or r.td_occ /= '1' then v.event.nf := '1'; if (r.mask.nf and not r.event.nf) = '1' then v.irq := '1'; end if; else v.event.nf := '0'; end if; -- Not empty detection if ((AM_EN = 0 or r.mode.amen = '0') and (r.rfreecnt /= FIFO_DEPTH or r.rd_free /= '1')) or (AM_EN = 1 and r.mode.amen = '1' and r.am.unread /= zero128(FIFO_DEPTH-1 downto 0)) then v.event.ne := '1'; if (r.mask.ne and not r.event.ne) = '1' then v.irq := '1'; end if; else v.event.ne := '0'; if AM_EN = 1 then v.am.lock := '0'; end if; end if; end if; --------------------------------------------------------------------------- -- Automated periodic transfer control --------------------------------------------------------------------------- if AM_EN = 1 and r.mode.amen = '1' then if r.am.active = '0' then -- Activation either from register write or external event. v.am.active := r.spio.astart or (astart and r.am.cfg.eact); v.am.cfg.act := v.am.active; v.am.rfreecnt := 0; for i in 0 to aloop loop if amask(i) = '1' then v.am.rfreecnt := v.am.rfreecnt+1; end if; end loop; v.am.skipdata := '0'; v.am.rxfull := '0'; v.am.cnt := unsigned(r.am.per); v.event.at := v.am.active; v.tdfi := (others => '0'); -- Check mask to see which word in the FIFO to start with. for i in 0 to aloop loop if amask(i) = '1' then if tstop1 = '0' then v.tdfi := conv_std_logic_vector(i, r.tdfi'length); end if; tstop1 := '1'; end if; end loop; if v.am.active = '1' then v.txdupd2 := '1'; tx_rd := '1'; v.tfreecnt := FIFO_DEPTH; for i in 0 to aloop loop if amask(i) = '1' then v.tfreecnt := v.tfreecnt-1; end if; end loop; end if; v.rdli := (others => '0'); for i in 0 to aloop loop if rstop1 = '0' then if amask(i) = '0' then v.rdli := v.rdli + 1; else rstop1 := '1'; end if; end if; end loop; v.cstart := v.am.active; else -- Receive fifo handling if r.am.rxfull = '1' then -- AM RX fifo is filled -- Move to receive queue if the queue is empty or if there is no -- requirement on sequential transfers and the queue is not locked. if (r.event.ne and (v.am.lock or r.am.cfg.seq)) = '0' then -- Queue is empty if syncram = 0 then v.rxfifo := r.am.rxfifo; else v.am.rxsel := not r.am.rxsel; end if; v.rdfi := (others => '0'); v.rfreecnt := r.am.rfreecnt; v.rd_free := '0'; v.am.rxfull := '0'; for i in 0 to aloop loop if amask(i) = '1' then v.am.unread(i) := '1'; end if; end loop; end if; if r.event.tip = '0' and r.am.at = '1' then v.event.at := '0'; end if; if (r.mask.at and r.event.at) = '1' then v.irq := '1'; end if; end if; if r.am.cfg.act = '0' then v.am.active := r.running; end if; v.am.cfg.eact := '0'; if (r.am.cnt = 0 and r.am.cfg.erpt = '0') or (astart = '1' and r.am.cfg.erpt = '1') then -- Only allowed to start new transfer if previous transfer(s) is finished if r.event.tip = '0' then if (not v.am.rxfull or r.am.cfg.strict) = '1' then v.am.cnt := unsigned(r.am.per); end if; if (not v.am.rxfull or (r.am.cfg.strict and not r.am.cfg.ovtb)) = '1' then -- Start transfer. Initialize indexes and fifo counter v.txdupd2 := '1'; tx_rd := '1'; v.am.cnt := unsigned(r.am.per); v.rdli := (others => '0'); for i in 0 to aloop loop if rstop2 = '0' then if amask(i) = '0' then v.rdli := v.rdli + 1; else rstop2 := '1'; end if; end if; end loop; v.tfreecnt := FIFO_DEPTH; v.am.rfreecnt := 0; for i in 0 to aloop loop if amask(i) = '1' then v.am.rfreecnt := v.am.rfreecnt+1; v.tfreecnt := v.tfreecnt-1; end if; end loop; v.tdfi := (others => '0'); -- Check mask to see which word in the FIFO to start with. for i in 0 to aloop loop if amask(i) = '1' then if tstop2 = '0' then v.tdfi := conv_std_logic_vector(i, r.tdfi'length); end if; tstop2 := '1'; end if; end loop; -- Skip incoming data if receive FIFO is full and OVDB is '1'. v.am.skipdata := v.am.rxfull and r.am.cfg.ovdb; if v.am.skipdata = '0' then -- Clear AM receive fifo if we will overwrite it. v.am.rfreecnt := FIFO_DEPTH; for i in 0 to aloop loop if amask(i) = '0' then v.am.rfreecnt := v.am.rfreecnt-1; end if; end loop; v.am.rxfull := '0'; end if; v.event.at := '1'; v.cstart := astart and r.am.cfg.erpt; end if; end if; else v.am.cnt := r.am.cnt - 1; end if; end if; end if; --------------------------------------------------------------------------- -- SCK filtering, only used in slave mode --------------------------------------------------------------------------- fsck := r.psck; if (r.mode.en and not r.mode.ms) = '1' then if (r.spii(1).sck xor r.psck) = '0' then reload := '1'; else -- Detected SCK change if r.divcnt = 0 then v.psck := r.spii(1).sck; fsck := r.spii(1).sck; fsck_chg := '1'; reload := '1'; else v.divcnt := r.divcnt - 1; end if; end if; elsif r.mode.en = '1' then v.psck := r.spii(1).sck; end if; --------------------------------------------------------------------------- -- SPI bus control --------------------------------------------------------------------------- if (r.mode.en and not r.running) = '1' and (r.mode.ms = '0' or r.divcnt = 0) then if r.mode.ms = '1' then if r.divcnt = 0 then v.spio.sck := r.mode.cpol; end if; v.spio.misooen := INPUT; if TW_EN = 0 or r.mode.tw = '0' then if OD_EN = 0 or r.mode.od = '0' then v.spio.mosioen := OUTPUT; end if; -- FIXME: is the below dangerous and should be changed instead when a -- transfer starts? if prot = 0 or orv(txdprot) = '0' then -- Standard mode if prot = 2 then v.spio.io2oen := INPUT; v.spio.io3oen := INPUT; end if; elsif prot = 1 or txdprot(0) = '1' then -- Dual mode v.spio.mosioen := txdio xor OUTPUT; v.spio.misooen := txdio xor OUTPUT; if prot = 2 then v.spio.io2oen := INPUT; v.spio.io3oen := INPUT; end if; else -- Quad mode v.spio.mosioen := txdio xor OUTPUT; v.spio.misooen := txdio xor OUTPUT; v.spio.io2oen := txdio xor OUTPUT; v.spio.io3oen := txdio xor OUTPUT; end if; else v.spio.mosioen := INPUT; end if; v.spio.sckoen := OUTPUT; if TW_EN = 1 then v.twdir := OUTPUT xor r.mode.tto; end if; else if (spisel or r.mode.tw) = '0' then v.spio.misooen := OUTPUT; if prot = 0 or orv(txdprot) = '0' then -- Standard mode if prot = 2 then v.spio.io2oen := INPUT; v.spio.io3oen := INPUT; end if; elsif prot = 1 or txdprot(0) = '1' then -- Dual mode v.spio.mosioen := txdio xor OUTPUT; v.spio.misooen := txdio xor OUTPUT; if prot = 2 then v.spio.io2oen := INPUT; v.spio.io3oen := INPUT; end if; else -- Quad mode v.spio.mosioen := txdio xor OUTPUT; v.spio.misooen := txdio xor OUTPUT; v.spio.io2oen := txdio xor OUTPUT; v.spio.io3oen := txdio xor OUTPUT; end if; else v.spio.misooen := INPUT; end if; if (not spisel and r.mode.tw and r.mode.tto) = '0' then v.spio.mosioen := INPUT; -- FIXME: Need to set correct direction here? else v.spio.mosioen := OUTPUT; end if; v.spio.sckoen := INPUT; if TW_EN = 1 then v.twdir := INPUT xor r.mode.tto; end if; end if; if ((((AM_EN = 0 or r.mode.amen = '0') or (AM_EN = 1 and r.mode.amen = '1' and r.am.active = '1')) and r.mode.ms = '1' and r.tfreecnt /= FIFO_DEPTH and r.txdupd = '0' and (AM_EN = 0 or r.txdupd2 = '0')) or slv_start(spisel, r.mode.cpol, fsck, fsck_chg)) then -- Underrun detection if r.tfreecnt = FIFO_DEPTH then v.uf := '1'; if (r.mask.un and not v.event.un) = '1' then v.irq := '1'; end if; v.event.un := '1'; end if; v.running := '1'; -- v.rxdprot := r.txdprot(1 downto 0); if r.mode.ms = '1' then if TW_EN = 0 or r.mode.tw = '0' then v.spio.mosioen := OUTPUT; else v.spio.mosioen := OUTPUT xor r.mode.tto; end if; change := not r.mode.cpha; -- Insert cycles when cpha = '0' to ensure proper setup -- time for first MOSI value in master mode. reload := not r.mode.cpha; end if; if (prot = 0 or orv(txdprot) = '0') and (TW_EN = 0 or r.mode.tw = '0') then -- Standard mode if prot = 2 then v.spio.io2oen := INPUT; v.spio.io3oen := INPUT; end if; elsif prot = 1 or txdprot(0) = '1' then -- Dual mode v.spio.mosioen := txdio xor OUTPUT; v.spio.misooen := txdio xor OUTPUT; if prot = 2 then v.spio.io2oen := INPUT; v.spio.io3oen := INPUT; end if; else -- Quad mode v.spio.mosioen := txdio xor OUTPUT; v.spio.misooen := txdio xor OUTPUT; v.spio.io2oen := txdio xor OUTPUT; v.spio.io3oen := txdio xor OUTPUT; end if; end if; v.cgcnt := (others => '0'); v.rbitcnt := (others => '0'); v.tbitcnt := (others => '0'); if r.mode.ms = '0' then update := not (r.mode.cpha or (fsck xor r.mode.cpol)); if r.mode.cpha = '0' then -- Prepare first bit v.tbitcnt := (others => '0'); if prot = 0 or orv(txdprot) = '0' then v.tbitcnt(0) := '1'; elsif prot = 1 or txdprot(0) = '1' then v.tbitcnt(1) := '1'; else v.tbitcnt(2) := '1'; end if; if v.running = '1' and (TW_EN = 0 or r.mode.tw = '0' or r.twdir = OUTPUT) then txshift := '1'; end if; end if; end if; -- samp and chng should not be changed on b2b if spisel /= '0' then v.samp := not r.mode.cpha; v.chng := r.mode.cpha; v.psck := r.mode.cpol; end if; end if; if AM_EN = 0 or r.mode.amen = '0' or r.am.cfg.ecgc = '0' then v.cgcntblock := '0'; else if r.cstart = '1' then v.cgcntblock := '0'; end if; end if; --------------------------------------------------------------------------- -- Clock generation, only in master mode --------------------------------------------------------------------------- if r.mode.ms = '1' and (r.running = '1' or r.divcnt /= 0) then -- The frequency of the SPI clock relative to the system clock is -- determined by the fact, div16 and pm register fields. -- -- With fact = 0 the fields have the same meaning as in the MPC83xx -- register interface. The clock is divided by 4*([PM]+1) and if div16 -- is set the clock is divided by 16*(4*([PM]+1)). -- -- With fact = 1 the core's register i/f is no longer compatible with -- the MPC83xx register interface. The clock is divided by 2*([PM]+1) and -- if div16 is set the clock is divided by 16*(2*([PM]+1)). -- -- The generated clock's duty cycle is always 50%. if r.divcnt = 0 then if ASEL_EN = 0 or r.aselcnt = 0 then -- Toggle SCK unless we are in a clock gap if (r.cgcnt = 0 and (AM_EN = 0 or r.cgcntblock = '0')) or r.spiolb.sck /= r.mode.cpol then v.spio.sck := not r.spiolb.sck; v.toggle := r.running; end if; if r.cgcnt /= 0 and (AM_EN = 0 or r.cgcntblock = '0') then v.cgcnt := r.cgcnt - 1; if ASEL_EN /= 0 and r.cgcnt = 1 then cgasel := r.mode.tac; end if; end if; elsif ASEL_EN = 1 then v.aselcnt := r.aselcnt - 1; end if; reload := '1'; else v.divcnt := r.divcnt - 1; end if; elsif r.mode.ms = '1' then v.divcnt := (others => '0'); end if; if reload = '1' then -- Reload clock scale counter v.divcnt(4 downto 0) := unsigned('0' & r.mode.pm) + 1; if (not r.mode.fact and r.mode.ms) = '1' then if r.mode.div16 = '1' then v.divcnt := shift_left(v.divcnt, 5) - 1; else v.divcnt := shift_left(v.divcnt, 1) - 1; end if; else if (r.mode.div16 and r.mode.ms) = '1' then v.divcnt := shift_left(v.divcnt, 4) - 1; else v.divcnt(9 downto 4) := (others => '0'); v.divcnt(3 downto 0) := unsigned(r.mode.pm); end if; end if; end if; --------------------------------------------------------------------------- -- Handle master operation. --------------------------------------------------------------------------- if r.mode.ms = '1' then -- Sample data if r.toggle = '1' then v.samp := not r.samp; sample := r.samp; end if; -- Change data on the clock flank... if v.toggle = '1' then v.chng := not r.chng; change := r.chng; end if; -- Detect multiple-master errors (mode-fault) if spisel = '0' then v.mode.en := '0'; v.mode.ms := '0'; v.event.mme := '1'; if (r.mask.mme and not r.event.mme) = '1' then v.irq := '1'; end if; v.running := '0'; v.event.tip := '0'; if AM_EN = 1 then v.event.at := '0'; end if; end if; -- Select input data if r.mode.loopb = '1' then indata(0) := r.spiolb.mosi; if prot /= 0 then indata(1) := r.spiolb.miso; end if; if prot = 2 then indata(2) := r.spiolb.io2; indata(3) := r.spiolb.io3; end if; elsif TW_EN = 1 and r.mode.tw = '1' then indata(0) := r.spii(1).mosi; else indata(0) := r.spii(1).miso; if prot /= 0 then if orv(rxdprot) = '1' then indata(0) := r.spii(1).mosi; end if; indata(1) := r.spii(1).miso; end if; if prot = 2 then indata(2) := r.spii(1).io2; indata(3) := r.spii(1).io3; end if; end if; end if; --------------------------------------------------------------------------- -- Handle slave operation --------------------------------------------------------------------------- if (r.mode.en and not r.mode.ms) = '1' then if spisel = '0' then if fsck_chg = '1' then sample := r.samp; v.samp := not r.samp; change := r.chng; v.chng := not r.chng; end if; indata(0) := r.spii(1).mosi; if prot /= 0 then indata(1) := r.spii(1).miso; end if; if prot = 2 then indata(2) := r.spii(1).io2; indata(3) := r.spii(1).io3; end if; end if; end if; --------------------------------------------------------------------------- -- Used in both master and slave operation --------------------------------------------------------------------------- if sample = '1' then -- Detect receive overflow if ((AM_EN = 0 or r.mode.amen = '0' ) and (r.rfreecnt = 0 and r.rd_free = '0')) or (AM_EN = 1 and r.mode.amen = '1' and r.am.rfreecnt = 0) or r.ov = '1' then if TW_EN = 0 or r.mode.tw = '0' or r.twdir = INPUT then -- Overflow event and IRQ v.ov := '1'; if r.ov = '0' then if (r.mask.ov and not r.event.ov) = '1' then v.irq := '1'; end if; v.event.ov := '1'; end if; end if; sample := '0'; -- Prevent sample below else sample := not r.mode.ms or r.mode.loopb; v.syncsamp(0) := not sample; end if; if r.rbitcnt >= len(log2(wlen+1)-1 downto 0) then v.rbitcnt := (others => '0'); if TW_EN = 1 then v.twdir := r.twdir xor not r.mode.loopb; end if; if (TW_EN = 0 or r.mode.tw = '0' or r.mode.loopb = '1' or (r.mode.tw = '1' and r.twdir = INPUT)) then v.incrdli := not r.ov; end if; if (TW_EN = 0 or r.mode.tw = '0' or r.mode.loopb = '1' or (TW_EN = 1 and r.mode.tw = '1' and (((r.mode.ms xor r.mode.tto) = '1' and r.twdir = INPUT) or ((r.mode.ms xor r.mode.tto) = '0' and r.twdir = OUTPUT)))) then if r.mode.cpha = '0' then v.cgcnt := unsigned(r.mode.cg & '0'); if ASEL_EN /= 0 then v.cgasel := r.mode.tac; end if; if AM_EN = 1 and r.mode.amen = '1' and r.am.cfg.ecgc = '1' then v.cgcntblock := '1'; end if; end if; v.ov := '0'; if r.tfreecnt = FIFO_DEPTH then v.running := '0'; -- When running with with SCK freq. at half the system freq. we are -- past the last edge here and SCK has transitioned from CPOL. -- Force controller into idle state, only applies to master mode. if (r.toggle and v.toggle) = '1' then v.toggle := '0'; v.spio.sck := r.mode.cpol; v.chng := r.chng; end if; end if; v.uf := '0'; end if; else if prot = 0 or orv(rxdprot) = '0' then v.rbitcnt := r.rbitcnt + 1; elsif prot = 1 or rxdprot(0) = '1' then v.rbitcnt := r.rbitcnt + 2; else v.rbitcnt := r.rbitcnt + 4; end if; end if; end if; -- Sample data line and put into shift register. if (r.syncsamp(1) or sample) = '1' then if prot = 0 or orv(rxdprot) = '0' then v.rxd := r.rxd(wlen-1 downto 0) & indata(0); elsif prot = 1 or rxdprot(0) = '1' then v.rxd := r.rxd(wlen-2 downto 0) & indata(0) & indata(1); else v.rxd := r.rxd(wlen-4 downto 0) & indata(0) & indata(1) & indata(2) & indata(3); end if; if ((r.syncsamp(1) and r.incrdli) or (sample and v.incrdli)) = '1' then v.rxdone := '1'; v.rxdone2 := '1'; v.incrdli := '0'; -- v.rxdprot := r.txdprot(1 downto 0); end if; end if; -- Put data into receive queue if ((AM_EN = 0 or (r.mode.amen and r.am.skipdata) = '0') and r.rxdone = '1') then if AM_EN = 1 and r.am.active = '1'then if not ((ignore > 0) and (spii_ignore = '1')) then -- Check mask, maybe we need to skip next word in fifo v.rdli := r.rdli + 1; v.am.rfreecnt := v.am.rfreecnt - 1; if DISCONT_AM_MASK then for i in 0 to aloop loop if i > conv_integer(r.rdli) and rstop3 = '0' then if amask(i) = '0' then v.rdli := v.rdli + 1; else rstop3 := '1'; end if; end if; end loop; end if; end if; else v.rdli := r.rdli + 1; v.rfreecnt := v.rfreecnt - 1; rx_rd := v.rd_free; end if; if syncram = 0 then if AM_EN = 1 and r.am.active = '1' then v.am.rxfifo(conv_integer(r.rdli)) := r.rxd; else v.rxfifo(conv_integer(r.rdli)) := r.rxd; end if; else rx_wr := '1'; end if; if r.running = '0' then if AM_EN = 1 then v.am.rxfull := r.am.active; end if; end if; end if; if AM_EN = 1 and r.mode.amen = '1' then if TW_EN = 0 or r.mode.tw = '0' or r.mode.tto = '0' then if r.rxdone = '1' then v.spio.aready := '1'; end if; else if r.twdir = '1' and r.twdir2 = '0' then v.spio.aready := '1'; end if; end if; end if; -- Special case to put data in receive queue for automatic -- transfer while in three wire mode with tto = 1 if AM_EN = 1 and TW_EN = 1 and r.mode.amen = '1' and r.mode.tw = '1' and r.running = '0' and r.rxdone2 = '1' and r.mode.tto = '1' and r.twdir = INPUT and r.mode.ms = '1' then v.am.rxfull := r.am.active; end if; -- Advance transmit queue if change = '1' then if TW_EN = 1 and r.mode.tw = '1' then v.spio.mosioen := r.twdir; end if; if r.tbitcnt >= len(log2(wlen+1)-1 downto 0) then if (TW_EN = 0 or r.mode.tw = '0' or r.mode.loopb = '1' or (TW_EN = 1 and r.mode.tw = '1' and (((r.mode.ms xor r.mode.tto) = '1' and r.twdir = INPUT) or ((r.mode.ms xor r.mode.tto) = '0' and r.twdir = OUTPUT)))) then if r.mode.cpha = '1' then v.cgcnt := unsigned(r.mode.cg & '0'); if ASEL_EN /= 0 then v.cgasel := r.mode.tac; end if; if AM_EN = 1 and r.mode.amen = '1' and r.am.cfg.ecgc = '1' then v.cgcntblock := '1'; end if; end if; end if; if (TW_EN = 0 or r.mode.tw = '0' or r.mode.loopb = '1' or r.twdir = OUTPUT) then if r.uf = '0' then if not ((ignore > 0) and (spii_ignore = '1')) then v.tfreecnt := v.tfreecnt + 1; end if; end if; v.txdupd := '1'; tx_rd := '1'; end if; v.tbitcnt := (others => '0'); else if prot = 0 or orv(txdprot) = '0' then v.tbitcnt := r.tbitcnt + 1; elsif prot = 1 or txdprot(0) = '1' then v.tbitcnt := r.tbitcnt + 2; else v.tbitcnt := r.tbitcnt + 4; end if; end if; if v.uf = '0' and (TW_EN = 0 or r.mode.tw = '0' or r.mode.loopb = '1' or r.twdir = OUTPUT) then txshift := v.running; end if; end if; if txshift = '1' then if prot = 0 or orv(txdprot) = '0' then v.txd := '1' & r.txd(wlen downto 1); elsif prot = 1 or txdprot(0) = '1' then v.txd := "11" & r.txd(wlen downto 2); else v.txd := "1111" & r.txd(wlen downto 4); -- FIXME: can break with wlen end if; end if; if AM_EN = 1 then if r.txdupd2 = '1' then tx_rd := '1'; v.txdupd := '1'; end if; end if; if r.txdupd = '1' then tx_rd := '1'; if r.txdbyp = '0' then if syncram = 0 then if AM_EN = 1 and r.mode.amen = '1' then v.txd := r.am.txfifo(conv_integer(r.tdfi)); else ctxfifo := r.txfifo(conv_integer(r.tdfi)); v.txd := ctxfifo(wlen downto 0); -- if prot /= 0 then -- v.txdprot := (others => '0'); -- v.txdprot(2) := ctxfifo(ctxfifo'left); -- v.txdprot(prot/2 downto 0) := ctxfifo(ctxfifo'left-1 downto ctxfifo'left-prot/2-1); -- end if; end if; else -- The first FIFO is always used when using syncrams, even in AM mode ctxfifo := tx_do(0); v.txd := ctxfifo(wlen downto 0); -- if prot /= 0 then -- v.txdprot := (others => '0'); -- v.txdprot(2) := ctxfifo(ctxfifo'left); -- v.txdprot(prot/2 downto 0) := ctxfifo(ctxfifo'left-1 downto ctxfifo'left-prot/2-1); -- end if; end if; end if; -- Data written to TD, bypass if v.txdbyp = '1' then v.txd := ntxd; -- if prot /= 0 then -- v.txdprot := r.cmd.sprot; -- if prot = 1 then v.txdprot(1) := '0'; end if; -- end if; end if; if r.tfreecnt /= FIFO_DEPTH then if AM_EN = 0 or r.mode.amen = '0' then v.tdfi := v.tdfi + 1; else -- Check mask, might need to skip next word if not (((ignore > 0) and (spii_ignore = '1'))) then if DISCONT_AM_MASK then for i in 0 to aloop loop if tstop3 = '0' and i > conv_integer(v.tdfi) then if amask(i) = '0' then v.tdfi := v.tdfi + 1; else tstop3 := '1'; end if; end if; end loop; end if; v.tdfi := v.tdfi + 1; end if; end if; elsif v.txdbyp = '0' then -- Bus idle value v.txd(0) := '1'; end if; -- if r.running = '0' then v.rxdprot := v.txdprot(1 downto 0); end if; end if; -- Transmit bit if (change or update) = '1' then if v.uf = '0' then if prot = 0 or orv(txdprot) = '0' then -- Legacy mode v.spio.miso := r.txd(0); v.spio.mosi := r.txd(0); if prot = 2 then v.spio.io2 := '1'; v.spio.io3 := '1'; end if; elsif prot = 1 or txdprot(0) = '1' then -- Dual mode v.spio.mosi := r.txd(0); v.spio.miso := r.txd(1); if prot = 2 then v.spio.io2 := '1'; v.spio.io3 := '1'; end if; else -- Quad mode v.spio.mosi := r.txd(0); v.spio.miso := r.txd(1); v.spio.io2 := r.txd(2); v.spio.io3 := r.txd(3); end if; if OD_EN = 1 and r.mode.od = '1' then if prot = 0 or orv(txdprot) = '0' or (orv(txdprot) and not txdio) = '1' then -- Only adapt for OD mode if we are in standard mode or if we are -- in dual or quad mode and direction is output if prot = 0 or orv(txdprot) = '0' then if (r.mode.ms or r.mode.tw) = '1' then v.spio.mosioen := r.txd(0) xor OUTPUT; else v.spio.misooen := r.txd(0) xor OUTPUT; end if; elsif prot = 1 or txdprot(0) = '1' then -- Dual mode v.spio.mosioen := r.txd(0) xor OUTPUT; v.spio.misooen := r.txd(1) xor OUTPUT; if prot = 2 then v.spio.io2oen := INPUT; v.spio.io3oen := INPUT; end if; else -- Quad mode v.spio.mosioen := r.txd(0) xor OUTPUT; v.spio.misooen := r.txd(1) xor OUTPUT; v.spio.io2oen := r.txd(2) xor OUTPUT; v.spio.io3oen := r.txd(3) xor OUTPUT ; end if; end if; end if; else v.spio.miso := '1'; v.spio.mosi := '1'; if prot = 2 then v.spio.io2 := '1'; v.spio.io3 := '1'; end if; if OD_EN = 1 and r.mode.od = '1' then v.spio.misooen := INPUT; v.spio.mosioen := INPUT; if prot = 2 then v.spio.io2oen := INPUT; v.spio.io3oen := INPUT; end if; end if; end if; end if; -- Transfer in progress interrupt generation if (not r.running and (r.ov2 or (r.rxdone2 or (not r.mode.ms and r.mode.tw)))) = '1' then if r.mode.ms = '0' or r.mode.cite = '0' or r.divcnt = 0 then v.event.tip := '0'; v.rxdone2 := '0'; end if; end if; if v.running = '1' then v.event.tip := '1'; end if; if (v.running and not r.event.tip and r.mask.tip and r.mode.en) = '1' then v.irq := '1'; end if; -- LST detection and interrupt generation if v.running = '0' and v.tfreecnt = FIFO_DEPTH and r.cmd.lst = '1' then v.event.lt := '1'; v.cmd.lst := '0'; if (r.mask.lt and not r.event.lt) = '1' then v.irq := '1'; end if; end if; --------------------------------------------------------------------------- -- Automatic slave select, only in master mode --------------------------------------------------------------------------- if ASEL_EN /= 0 then if (r.mode.ms and r.mode.asel) = '1' then if ((not r.running and v.running) or -- Transfer start or (r.event.tip and not v.event.tip) or -- transfer end or (v.running and (cgasel or -- End or start of CG (r.cgasel and not (r.spiolb.sck xor r.mode.cpol))))) = '1' then v.slvsel := r.aslvsel; v.aslvsel := r.slvsel; v.cgasel := '0'; end if; -- May need to delay start of transfer if ((not r.running and v.running) or cgasel) = '1' then -- Transfer start v.aselcnt := unsigned(r.mode.aseldel); end if; else v.cgasel := '0'; v.aselcnt := (others => '0'); end if; end if; -- Do not toggle outputs in loopback mode if (r.mode.loopb = '1' or (r.mode.tw = '1' and TW_EN = 1 and r.twdir = INPUT)) then v.spio.mosioen := INPUT; v.spio.misooen := INPUT; if prot = 2 then v.spio.io2oen := INPUT; v.spio.io3oen := INPUT; end if; end if; if r.mode.loopb = '1' then v.spio.sckoen := INPUT; end if; -- When driving in OD mode, always drive low. if OD_EN = 1 and (r.mode.od and not r.mode.loopb) = '1' then v.spio.miso := v.spio.miso and not r.mode.od; v.spio.mosi := v.spio.mosi and not r.mode.od; if prot = 2 then v.spio.io2 := v.spio.io2 and not r.mode.od; v.spio.io3 := v.spio.io3 and not r.mode.od; end if; end if; -- Core is disabled if ((not ASYNC_RESET) and (not RESET_ALL) and rstn = '0') or (r.mode.en = '0') then v.tfreecnt := FIFO_DEPTH; v.rfreecnt := FIFO_DEPTH; v.tdfi := RES.tdfi; v.rdfi := RES.rdfi; v.tdli := RES.tdli; v.rdli := RES.rdli; v.rd_free := RES.rd_free; v.td_occ := RES.td_occ; v.cmd := RES.cmd; v.uf := RES.uf; v.ov := RES.ov; v.running := RES.running; v.event.tip := RES.event.tip; v.incrdli := RES.incrdli; if TW_EN = 1 then v.twdir := RES.twdir; end if; v.spio.miso := RES.spio.miso; v.spio.mosi := RES.spio.mosi; if prot = 2 then v.spio.io2 := RES.spio.io2; v.spio.io3 := RES.spio.io3; end if; if syncrst = 1 or (r.mode.en = '0') then v.spio.misooen := RES.spio.misooen; v.spio.mosioen := RES.spio.mosioen; v.spio.sckoen := RES.spio.sckoen; if prot = 2 then v.spio.io2oen := RES.spio.io2oen; v.spio.io3oen := RES.spio.io3oen; end if; end if; if AM_EN = 1 then v.event.at := RES.event.at; end if; -- Need to assign samp, chng and psck here if spisel is low when the -- core is enabled v.samp := not r.mode.cpha; v.chng := r.mode.cpha; v.psck := r.mode.cpol; if AM_EN = 1 then v.am.active := RES.am.active; v.am.cfg.act := RES.am.cfg.act; v.am.cfg.eact := RES.am.cfg.eact; v.am.unread := RES.am.unread; v.am.rxsel := RES.am.rxsel; end if; v.rxdone2 := '0'; v.divcnt := (others => '0'); end if; -- Chip reset if (not ASYNC_RESET) and (not RESET_ALL) and (rstn = '0') then v.mode := RES.mode; v.event.tip := RES.event.tip; v.event.lt := RES.event.lt; v.event.ov := RES.event.ov; v.event.un := RES.event.un; v.event.mme := RES.event.mme; v.event.ne := RES.event.ne; v.event.nf := RES.event.nf; v.mask := RES.mask; if AM_EN = 1 then v.event.at := RES.event.at; if PROG_AM_MASK then v.am.mask_shdw := RES.am.mask_shdw; end if; v.am.per := RES.am.per; v.am.cfg := RES.am.cfg; v.am.rxread := RES.am.rxread; v.am.txwrite := RES.am.txwrite; v.am.txread := RES.am.txread; v.am.apbaddr := RES.am.apbaddr; v.am.rxsel := RES.am.rxsel; v.cgcntblock := RES.cgcntblock; end if; v.cmd := RES.cmd; if syncrst = 1 then v.slvsel := RES.slvsel; end if; v.cgcnt := RES.cgcnt; v.rbitcnt := RES.rbitcnt; v.tbitcnt := RES.tbitcnt; v.txd := RES.txd; -- v.txdprot := RES.txdprot; -- v.rxdprot := RES.rxdprot; end if; -- Drive unused bit if open drain mode is not supported if OD_EN = 0 then v.mode.od := '0'; end if; -- Drive unused bits if automode is not supported if AM_EN = 0 then v.mode.amen := '0'; -- v.am.cfg.seq := '0'; v.am.cfg.strict := '0'; v.am.cfg.ovtb := '0'; v.am.cfg.ovdb := '0'; v.am.cfg.act := '0'; v.am.cfg.eact := '0'; v.am.per := (others => '0'); v.am.active := '0'; v.am.lock := '0'; v.am.skipdata := '0'; v.am.rxfull := '0'; v.am.rfreecnt := 0; v.event.at := '0'; v.am.unread := (others=>'0'); v.am.cfg.erpt := '0'; v.am.cfg.lock := '0'; v.am.cfg.ecgc := '0'; v.am.cnt := (others=>'0'); v.am.rxread := '0'; v.am.txwrite := '0'; v.am.txread := '0'; v.am.apbaddr := (others => '0'); v.am.rxsel := '0'; v.mask.at := '0'; v.cstart := '0'; end if; if AM_EN = 0 or not PROG_AM_MASK then v.am.mask := (others=>'0'); v.am.mask_shdw := (others=>'0'); end if; -- Drive unused bits if automatic slave select is not enabled if ASEL_EN = 0 then v.mode.asel := '0'; v.aslvsel := (others => '0'); v.mode.aseldel := (others => '0'); v.mode.tac := '0'; v.aselcnt := (others => '0'); v.cgasel := '0'; end if; -- if prot = 0 then -- v.txdprot := (others => '0'); v.rxdprot := (others => '0'); -- end if; if prot /= 2 then v.spio.io2 := '0'; v.spio.io2oen := INPUT; v.spio.io3 := '0'; v.spio.io3oen := INPUT; end if; -- Drive unused bits if three-wire mode is not enabled if TW_EN = 0 then v.mode.tw := '0'; v.mode.tto := '0'; v.twdir := INPUT; end if; if TW_EN = 0 or AM_EN = 0 then v.twdir2 := INPUT; end if; if SLVSEL_EN = 0 then v.slvsel := (others => '1'); end if; -- Propagate core enable bit v.spio.enable := r.mode.en; -- Synchronize inputs coming from off-chip v.spii(0) := (spii_miso, spii_mosi, spii_sck, spii_spisel, spii_io2, spii_io3); if prot /= 2 then v.spii(0).io2 := '0'; v.spii(0).io3 := '0'; end if; v.spii(1) := r.spii(0); -- Outputs to RAMs if syncram = 0 then rx_di <= (others => (others => '0')); tx_di <= (others => (others => '0')); rx_ra <= (others => (others => '0')); rx_wa <= (others => (others => '0')); tx_ra <= (others => (others => '0')); tx_wa <= (others => (others => '0')); rx_read <= (others => '0'); rx_write <= (others => '0'); tx_read <= (others => '0'); tx_write <= (others => '0'); else -- TX RAM(s) write -- TX RAM(s) are either written from TX register or AM TX area for i in 0 to automode loop ctxfifo(ctxfifo'left) := r.cmd.sprot(2); ctxfifo(ctxfifo'left-1 downto ctxfifo'left-prot/2-1) := r.cmd.sprot(prot/2 downto 0); ctxfifo(wlen downto 0) := ntxd; tx_di(i) <= ctxfifo; end loop; for i in 0 to automode loop tx_wa(i) <= r.tdli; end loop; tx_write(0) <= tx_wr; if AM_EN /= 0 then -- Auto mode present -- Write from AM register interface writes both RAMs -- Write from TXD register writes RAM 0 tx_write(automode) <= r.am.txwrite; tx_write(0) <= tx_wr or r.am.txwrite; if r.am.txwrite = '1' then for i in 0 to automode loop tx_wa(i) <= r.am.apbaddr; end loop; end if; end if; -- TX RAM(s) read -- First RAM is read by bit shift logic tx_read(0) <= tx_rd; tx_ra(0) <= r.tdfi; if AM_EN /= 0 then -- Second RAM is read from register interface tx_read(automode) <= v.am.txread or r.am.txread; tx_ra(automode) <= v.am.apbaddr; end if; -- RX RAM(s) write -- RX RAM(s) is always written from receive shift register for i in 0 to automode loop rx_di(i) <= r.rxd; rx_wa(i) <= r.rdli; end loop; rx_write(0) <= rx_wr; if AM_EN /= 0 then rx_write(automode) <= '0'; end if; if AM_EN /= 0 and r.mode.amen = '1' then -- AM active -- Handle writes from bit shift logic if r.am.rxsel = '0' then rx_write(0) <= rx_wr; rx_write(automode) <= '0'; else rx_write(0) <= '0'; rx_write(automode) <= rx_wr; end if; end if; -- RX RAM(s) are read via register interface for i in 0 to automode loop rx_ra(i) <= r.rdfi; rx_read(i) <= rx_rd; end loop; if AM_EN /= 0 and r.mode.amen = '1' then if r.am.rxsel = '0' then rx_read(0) <= '0'; rx_read(automode) <= v.am.rxread; if v.am.rxread = '1' then rx_ra(automode) <= v.am.apbaddr; end if; else rx_read(0) <= v.am.rxread; rx_read(automode) <= '0'; if v.am.rxread = '1' then rx_ra(0) <= v.am.apbaddr; end if; end if; end if; if scantest = 1 and (apbi_scanen and apbi_testen) = '1' then rx_read <= (others => '0'); rx_write <= (others => '0'); tx_read <= (others => '0'); tx_write <= (others => '0'); end if; end if; v.spiolb.mosi := v.spio.mosi; v.spiolb.sck := v.spio.sck; if prot /= 0 then v.spiolb.miso := v.spio.miso; else v.spiolb.miso := '0'; end if; if prot = 2 then v.spiolb.io2 := v.spio.io2; v.spiolb.io3 := v.spio.io3; else v.spiolb.io2 := '0'; v.spiolb.io3 := '0'; end if; -- Update registers rin <= v; -- Update outputs apbo_prdata <= apbout; apbo_pirq <= r.irq; slvsel <= r.slvsel; spio_miso <= r.spio.miso; spio_misooen <= r.spio.misooen; spio_mosi <= r.spio.mosi; spio_mosioen <= r.spio.mosioen; spio_sck <= r.spio.sck; spio_sckoen <= r.spio.sckoen; spio_enable <= r.spio.enable; spio_astart <= r.spio.astart; spio_aready <= r.spio.aready; spio_io2 <= r.spio.io2; spio_io2oen <= r.spio.io2oen; spio_io3 <= r.spio.io3; spio_io3oen <= r.spio.io3oen; if scantest = 1 and apbi_testen = '1' then spio_misooen <= apbi_testoen; spio_mosioen <= apbi_testoen; spio_sckoen <= apbi_testoen; if prot = 2 then spio_io2oen <= apbi_testoen; spio_io3oen <= apbi_testoen; end if; end if; end process comb; -- FIFOs fiforams : if syncram /= 0 generate fifoloop : for i in 0 to automode generate noft : if ft = 0 generate rxfifo : syncram_2p generic map ( tech => memtech, abits => fdepth, dbits => wlen+1, sepclk => 0, wrfst => 1) port map ( rclk => clk, renable => rx_read(i), raddress => rx_ra(i), dataout => rx_do(i), wclk => clk, write => rx_write(i), waddress => rx_wa(i), datain => rx_di(i)); -- testin => testin); txfifo : syncram_2p generic map ( tech => memtech, abits => fdepth, dbits => wlen+1+spip_bits, sepclk => 0, wrfst => 1) port map ( rclk => clk, renable => tx_read(i), raddress => tx_ra(i), dataout => tx_do(i), wclk => clk, write => tx_write(i), waddress => tx_wa(i), datain => tx_di(i)); -- testin => testin); end generate noft; ftfifos : if ft /= 0 generate ftrxfifo : syncram_2pft generic map ( tech => memtech, abits => fdepth, dbits => wlen+1, sepclk => 0, wrfst => 1, ft => ft) port map ( rclk => clk, renable => rx_read(i), raddress => rx_ra(i), dataout => rx_do(i), wclk => clk, write => rx_write(i), waddress => rx_wa(i), datain => rx_di(i), error => open); -- testin => testin); fttxfifo : syncram_2pft generic map ( tech => memtech, abits => fdepth, dbits => wlen+1+spip_bits, sepclk => 0, wrfst => 1, ft => ft) port map ( rclk => clk, renable => tx_read(i), raddress => tx_ra(i), dataout => tx_do(i), wclk => clk, write => tx_write(i), waddress => tx_wa(i), datain => tx_di(i), error => open); -- testin => testin); end generate ftfifos; end generate fifoloop; end generate fiforams; nofiforams : if syncram = 0 generate rx_do <= (others => (others => '0')); tx_do <= (others => (others => '0')); end generate; -- Registers syncrregs : if not ASYNC_RESET generate reg: process (clk, arstn) begin -- process reg if rising_edge(clk) then r <= rin; if rstn = '0' then r.spio.sck <= RES.spio.sck; r.rbitcnt <= RES.rbitcnt; r.tbitcnt <= RES.tbitcnt; if RESET_ALL then r <= RES; -- Do not use synchronous reset for sync. registers r.spii <= rin.spii; end if; end if; end if; if syncrst = 0 and arstn = '0' then r.spio.misooen <= RES.spio.misooen; r.spio.mosioen <= RES.spio.mosioen; r.spio.sckoen <= RES.spio.sckoen; if prot = 2 then r.spio.io2oen <= RES.spio.io2oen; r.spio.io3oen <= RES.spio.io3oen; end if; if SLVSEL_EN /= 0 then r.slvsel <= RES.slvsel; end if; end if; end process reg; end generate; asyncrregs : if ASYNC_RESET generate reg: process (clk, arstn) begin -- process reg if arstn = '0' then -- r <= RES; -- Do not use asynchronous reset for sync. registers r.mode <= RES.mode; r.event <= RES.event; r.mask <= RES.mask; r.cmd <= RES.cmd; r.td <= RES.td; r.rd <= RES.rd; r.slvsel <= RES.slvsel; r.aslvsel <= RES.aslvsel; r.uf <= RES.uf; r.ov <= RES.ov; r.td_occ <= RES.td_occ; r.rd_free <= RES.rd_free; r.txfifo <= RES.txfifo; r.rxfifo <= RES.rxfifo; r.rxd <= RES.rxd; r.txd <= RES.txd; r.txdupd <= RES.txdupd; r.txdbyp <= RES.txdbyp; r.toggle <= RES.toggle; r.samp <= RES.samp; r.chng <= RES.chng; r.psck <= RES.psck; r.twdir <= RES.twdir; r.syncsamp <= RES.syncsamp; r.incrdli <= RES.incrdli; r.rxdone <= RES.rxdone; r.rxdone2 <= RES.rxdone2; r.running <= RES.running; r.ov2 <= RES.ov2; r.tfreecnt <= RES.tfreecnt; r.rfreecnt <= RES.rfreecnt; r.tdfi <= RES.tdfi; r.rdfi <= RES.rdfi; r.tdli <= RES.tdli; r.rdli <= RES.rdli; r.rbitcnt <= RES.rbitcnt; r.tbitcnt <= RES.tbitcnt; r.divcnt <= RES.divcnt; r.cgcnt <= RES.cgcnt; r.cgcntblock <= RES.cgcntblock; r.aselcnt <= RES.aselcnt; r.cgasel <= RES.cgasel; r.irq <= RES.irq; r.am <= RES.am; r.spio <= RES.spio; r.spiolb <= RES.spiolb; r.astart <= RES.astart; r.cstart <= RES.cstart; r.txdupd2 <= RES.txdupd2; r.twdir2 <= RES.twdir2; elsif rising_edge(clk) then --r <= rin; r.mode <= rin.mode; r.event <= rin.event; r.mask <= rin.mask; r.cmd <= rin.cmd; r.td <= rin.td; r.rd <= rin.rd; r.slvsel <= rin.slvsel; r.aslvsel <= rin.aslvsel; r.uf <= rin.uf; r.ov <= rin.ov; r.td_occ <= rin.td_occ; r.rd_free <= rin.rd_free; r.txfifo <= rin.txfifo; r.rxfifo <= rin.rxfifo; r.rxd <= rin.rxd; r.txd <= rin.txd; r.txdupd <= rin.txdupd; r.txdbyp <= rin.txdbyp; r.toggle <= rin.toggle; r.samp <= rin.samp; r.chng <= rin.chng; r.psck <= rin.psck; r.twdir <= rin.twdir; r.syncsamp <= rin.syncsamp; r.incrdli <= rin.incrdli; r.rxdone <= rin.rxdone; r.rxdone2 <= rin.rxdone2; r.running <= rin.running; r.ov2 <= rin.ov2; r.tfreecnt <= rin.tfreecnt; r.rfreecnt <= rin.rfreecnt; r.tdfi <= rin.tdfi; r.rdfi <= rin.rdfi; r.tdli <= rin.tdli; r.rdli <= rin.rdli; r.rbitcnt <= rin.rbitcnt; r.tbitcnt <= rin.tbitcnt; r.divcnt <= rin.divcnt; r.cgcnt <= rin.cgcnt; r.cgcntblock <= rin.cgcntblock; r.aselcnt <= rin.aselcnt; r.cgasel <= rin.cgasel; r.irq <= rin.irq; r.am <= rin.am; r.spio <= rin.spio; r.spiolb <= rin.spiolb; r.astart <= rin.astart; r.cstart <= rin.cstart; r.txdupd2 <= rin.txdupd2; r.twdir2 <= rin.twdir2; end if; end process reg; reg2: process (clk, arstn) begin -- process reg if rising_edge(clk) then r.spii <= rin.spii; end if; end process reg2; end generate; end architecture rtl;