mirror of
https://github.com/lcbcFoo/ReonV.git
synced 2025-04-22 12:37:06 -04:00
2473 lines
87 KiB
VHDL
2473 lines
87 KiB
VHDL
------------------------------------------------------------------------------
|
|
-- 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;
|
|
|