ReonV/lib/gaisler/spi/spictrlx.vhd

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;