ReonV/lib/gaisler/spi/spi_flash.vhd

576 lines
20 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: spi_flash
-- File: spi_flash.vhd
-- Author: Jan Andersson - Aeroflex Gaisler AB
-- jan@gaisler.com
--
-- Description:
--
-- SPI flash simulation models.
--
-- +--------------------------------------------------------+
-- | ftype | Memory device |
-- +--------+-----------------------------------------------+
-- | 1 | SD card |
-- +--------+-----------------------------------------------+
-- | 3 | Simple SPI |
-- +--------+-----------------------------------------------+
-- | 4 | SPI memory device |
-- +--------+-----------------------------------------------+
--
-- For ftype => 4, the memoffset generic can be used to specify an address
-- offset that till be automatically be removed by the memory model. For
-- instance, memoffset => 16#1000# and an access to 0x1000 will read the
-- internal memory array at offset 0x0. This is a quick hack to support booting
-- from SPIMCTRL that has an offset specified and not having to modify the
-- SREC.
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use std.textio.all;
library grlib, gaisler;
use grlib.stdlib.all;
use grlib.stdio.all;
--use gaisler.sim.all;
entity spi_flash is
generic (
ftype : integer := 0; -- Flash type
debug : integer := 0; -- Debug output
fname : string := "prom.srec"; -- File to read from
readcmd : integer := 16#0B#; -- SPI memory device read command
dummybyte : integer := 1;
dualoutput : integer := 0;
memoffset : integer := 0); -- Addr. offset automatically removed
-- by Flash model
port (
sck : in std_ulogic;
di : inout std_logic;
do : inout std_logic;
csn : inout std_logic;
-- Test control inputs
sd_cmd_timeout : in std_ulogic := '0';
sd_data_timeout : in std_ulogic := '0'
);
end spi_flash;
architecture sim of spi_flash is
-- Description: Simple, incomplete, model of SD card
procedure simple_sd_model (
constant dbg : in integer;
signal sck : in std_ulogic;
signal di : in std_ulogic;
signal do : out std_ulogic;
signal csn : in std_ulogic;
-- Test control inputs
signal cmd_to : in std_ulogic; -- force command response timeout
signal data_to : in std_ulogic) is -- force data token timeout
type sd_state_type is (idle, wait_cmd55, wait_acmd41, wait_cmd16,
wait_cmd17);
type response_type is array (0 to 10) of std_logic_vector(7 downto 0);
variable state : sd_state_type := idle;
variable received_command : std_ulogic := '0';
variable respond : std_ulogic := '0';
variable response : response_type;
variable resp_size : integer;
variable indata : std_logic_vector(7 downto 0);
variable command : std_logic_vector(47 downto 0);
variable index : integer;
variable bcnt : integer;
constant CMD0 : std_logic_vector(5 downto 0) := "000000";
constant CMD16 : std_logic_vector(5 downto 0) := "010000";
constant CMD17 : std_logic_vector(5 downto 0) := "010001";
constant CMD55 : std_logic_vector(5 downto 0) := "110111";
constant ACMD41 : std_logic_vector(5 downto 0) := "101001";
constant R1 : std_logic_vector(7 downto 0) := X"00";
constant DATA_TOKEN : std_logic_vector(7 downto 0) := X"FE";
constant DATA_ERR_TOKEN : std_logic_vector(7 downto 0) := X"01";
begin -- simple_sd_model
loop
if csn /= '0' then wait until csn = '0'; end if;
index := 0; command := (others => '0');
-- Receive data
do <= '1';
while received_command = '0' and csn = '0' loop
wait until rising_edge(sck);
indata := indata(6 downto 0) & di;
index := index + 1;
if index = 8 then -- Received a byte
command := command(39 downto 0) & indata;
if dbg /= 0 then
Print(time'image(now) & ": simple_sd_model: received byte: " &
tost(indata));
end if;
if (command(47 downto 46) = "01" and command(7 downto 0) = X"95") then
received_command := '1';
end if;
index := 0;
end if;
end loop;
if received_command = '1' then
case state is
when idle =>
if command(45 downto 40) = CMD0 then
if dbg /= 0 then
Print(time'image(now) & ": simple_sd_model: received CMD0");
end if;
if cmd_to = '0' then
state := wait_cmd55;
end if;
response(0) := R1;
response(1) := (others => '1');
resp_size := 2;
respond := not cmd_to;
else
if dbg /= 0 then
Print(time'image(now) & ": simple_sd_model: received unexpected CMD" &
tost(conv_integer(command(45 downto 40))));
end if;
end if;
when wait_cmd55 =>
if command(45 downto 40) = CMD55 then
if dbg /= 0 then
Print(time'image(now) & ": simple_sd_model: received CMD55");
end if;
state := wait_acmd41;
response(0) := R1;
response(1) := (others => '1');
resp_size := 2;
respond := not cmd_to;
else
if dbg /= 0 then
Print(time'image(now) & ": simple_sd_model: received unexpected CMD" &
tost(conv_integer(command(45 downto 40))));
end if;
end if;
when wait_acmd41 =>
if command(45 downto 40) = ACMD41 then
if dbg /= 0 then
Print(time'image(now) & ": simple_sd_model: received CMD41");
end if;
if cmd_to = '0' then
state := wait_cmd16;
else
state := idle;
end if;
response(0) := R1;
response(1) := (others => '1');
resp_size := 2;
respond := not cmd_to;
else
if dbg /= 0 then
Print(time'image(now) & ": simple_sd_model: received unexpected CMD" &
tost(conv_integer(command(45 downto 40))));
end if;
end if;
when wait_cmd16 =>
if command(45 downto 40) = CMD16 then
if dbg /= 0 then
Print(time'image(now) & ": simple_sd_model: received CMD16");
Print(time'image(now) & ": simple_sd_model: BLOCKLEN set to " &
tost(conv_integer(command(39 downto 8))));
end if;
state := wait_cmd17;
response(0) := R1;
response(1) := (others => '1');
resp_size := 2;
respond := not cmd_to;
else
if dbg /= 0 then
Print(time'image(now) & ": simple_sd_model: received unexpected CMD" &
tost(conv_integer(command(45 downto 40))));
end if;
end if;
when wait_cmd17 =>
if command(45 downto 40) = CMD17 then
if dbg /= 0 then
Print(time'image(now) & ": simple_sd_model: received CMD17");
Print(time'image(now) & ": simple_sd_model: Read from address " &
tost(conv_integer(command(39 downto 8))));
end if;
response(0) := R1;
response(1) := (others => '1');
response(2) := (others => '1');
response(3) := DATA_TOKEN;
-- Data response is address
response(4) := command(39 downto 32);
response(5) := command(31 downto 24);
response(6) := command(23 downto 16);
response(7) := command(15 downto 8);
if data_to = '1' then
resp_size := 1;
else
resp_size := 8;
end if;
respond := not cmd_to;
elsif command(45 downto 40) = CMD0 then
if dbg /= 0 then
Print(time'image(now) & ": simple_sd_model: received CMD0");
end if;
if cmd_to = '0' then
state := wait_cmd55;
end if;
response(0) := R1;
response(1) := (others => '1');
resp_size := 2;
respond := not cmd_to;
else
if dbg /= 0 then
Print(time'image(now) & ": simple_sd_model: received unexpected CMD" &
tost(conv_integer(command(45 downto 40))));
end if;
end if;
end case;
received_command := '0';
end if;
if respond = '1' then
bcnt := 0;
while resp_size > bcnt loop
if dbg /= 0 then
Print(time'image(now) & ": simple_sd_model: Responding with " &
tost(response(bcnt)));
end if;
index := 0;
while index < 8 loop
wait until falling_edge(sck);
do <= response(bcnt)(7);
response(bcnt)(7 downto 1) := response(bcnt)(6 downto 0);
index := index + 1;
end loop;
bcnt := bcnt + 1;
end loop;
respond := '0';
wait until rising_edge(sck);
else
do <= '1';
end if;
end loop;
end simple_sd_model;
-- purpose: Simple, incomplete, model of SPI Flash device
procedure simple_spi_flash_model (
constant dbg : in integer;
constant readcmd : in integer;
constant dummybyte : in boolean;
constant dualoutput : in boolean;
signal sck : in std_ulogic;
signal di : inout std_ulogic;
signal do : out std_ulogic;
signal csn : in std_ulogic) is
constant readinst : std_logic_vector(7 downto 0) :=
conv_std_logic_vector(readcmd, 8);
variable received_command : std_ulogic := '0';
variable respond : std_ulogic := '0';
variable response : std_logic_vector(31 downto 0);
variable indata : std_logic_vector(7 downto 0);
variable command : std_logic_vector(39 downto 0);
variable index : integer;
begin -- simple_spi_flash_model
di <= 'Z'; do <= 'Z';
loop
if csn /= '0' then wait until csn = '0'; end if;
index := 0; command := (others => '0');
while received_command = '0' and csn = '0' loop
wait until rising_edge(sck);
indata := indata(6 downto 0) & di;
index := index + 1;
if index = 8 then
command := command(31 downto 0) & indata;
if dbg /= 0 then
Print(time'image(now) & ": simple_spi_flash_model: received byte: " &
tost(indata));
end if;
if ((dummybyte and command(39 downto 32) = readinst) or
(not dummybyte and command(31 downto 24) = readinst)) then
received_command := '1';
end if;
index := 0;
end if;
end loop;
if received_command = '1' then
response := (others => '0');
if dummybyte then
response(23 downto 0) := command(31 downto 8);
else
response(23 downto 0) := command(23 downto 0);
end if;
index := 31 - conv_integer(response(1 downto 0)) * 8;
response(1 downto 0) := (others => '0');
while csn = '0' loop
while index >= 0 and csn = '0' loop
wait until falling_edge(sck) or csn = '1';
if dualoutput then
do <= response(index);
di <= response(index-1);
index := index - 2;
else
do <= response(index);
index := index - 1;
end if;
end loop;
index := 31;
response := response + 4;
end loop;
if dualoutput then
di <= 'Z';
end if;
received_command := '0';
else
do <= '1';
end if;
end loop;
end simple_spi_flash_model;
-- purpose: SPI memory device that reads input from prom.srec
procedure spi_memory_model (
constant dbg : in integer;
constant readcmd : in integer;
constant dummybyte : in boolean;
constant dualoutput : in boolean;
signal sck : in std_ulogic;
signal di : inout std_ulogic;
signal do : inout std_ulogic;
signal csn : in std_ulogic) is
constant readinst : std_logic_vector(7 downto 0) :=
conv_std_logic_vector(readcmd, 8);
variable received_command : std_ulogic := '0';
variable respond : std_ulogic := '0';
variable response : std_logic_vector(31 downto 0);
variable address : std_logic_vector(23 downto 0);
variable indata : std_logic_vector(7 downto 0);
variable command : std_logic_vector(39 downto 0);
variable index : integer;
file fload : text open read_mode is fname;
variable fline : line;
variable fchar : character;
variable rtype : std_logic_vector(3 downto 0);
variable raddr : std_logic_vector(31 downto 0);
variable rlen : std_logic_vector(7 downto 0);
variable rdata : std_logic_vector(0 to 127);
variable wordaddr : integer;
type mem_type is array (0 to 8388607) of std_logic_vector(31 downto 0);
variable mem : mem_type := (others => (others => '1'));
begin -- spi_memory_model
di <= 'Z'; do <= 'Z';
-- Load memory data from file
while not endfile(fload) loop
readline(fload, fline);
read(fline, fchar);
if fchar /= 'S' or fchar /= 's' then
hread(fline, rtype);
hread(fline, rlen);
raddr := (others => '0');
case rtype is
when "0001" =>
hread(fline, raddr(15 downto 0));
when "0010" =>
hread(fline, raddr(23 downto 0));
when "0011" =>
hread(fline, raddr);
raddr(31 downto 24) := (others => '0');
when others => next;
end case;
hread(fline, rdata);
for i in 0 to 3 loop
mem(conv_integer(raddr(31 downto 2)+i)) :=
rdata(i*32 to i*32+31);
end loop;
end if;
end loop;
loop
if csn /= '0' then wait until csn = '0'; end if;
index := 0; command := (others => '0');
while received_command = '0' and csn = '0' loop
wait until rising_edge(sck);
indata := indata(6 downto 0) & di;
index := index + 1;
if index = 8 then
command := command(31 downto 0) & indata;
if dbg /= 0 then
Print(time'image(now) & ": spi_memory_model: received byte: " &
tost(indata));
end if;
if ((dummybyte and command(39 downto 32) = readinst) or
(not dummybyte and command(31 downto 24) = readinst)) then
received_command := '1';
end if;
index := 0;
end if;
end loop;
if received_command = '1' then
response := (others => '0');
if dummybyte then
address := command(31 downto 8);
else
address := command(23 downto 0);
end if;
if dbg /= 0 then
Print(time'image(now) & ": spi_memory_model: received address: " &
tost(address));
if memoffset /= 0 then
Print(time'image(now) & ": spi_memory_model: address after removed offset " &
tost(address-memoffset));
end if;
end if;
if memoffset /= 0 then
address := address - memoffset;
end if;
index := 31 - conv_integer(address(1 downto 0)) * 8;
while csn = '0' loop
response := mem(conv_integer(address(23 downto 2)));
if dbg /= 0 then
Print(time'image(now) & ": spi_memory_model: responding with data: " &
tost(response(index downto 0)));
end if;
while index >= 0 and csn = '0' loop
wait until falling_edge(sck) or csn = '1';
if dualoutput then
do <= response(index);
di <= response(index-1);
index := index - 2;
else
do <= response(index);
index := index - 1;
end if;
end loop;
index := 31;
address := address + 4;
end loop;
if dualoutput then
di <= 'Z';
end if;
do <= 'Z';
received_command := '0';
else
do <= 'Z';
end if;
end loop;
end spi_memory_model;
signal vdd : std_ulogic := '1';
signal gnd : std_ulogic := '0';
begin -- sim
-- ftype0: if ftype = 0 generate
-- csn <= 'Z';
-- di <= 'Z';
-- flash0 : s25fl064a
-- generic map (tdevice_PU => 1 us,
-- TimingChecksOn => true,
-- MsgOn => debug = 1,
-- UserPreLoad => true)
-- port map (SCK => sck, SI => di, CSNeg => csn, HOLDNeg => vdd,
-- WNeg => vdd, SO => do);
-- end generate ftype0;
ftype1: if ftype = 1 generate
csn <= 'H';
di <= 'Z';
simple_sd_model(debug, sck, di, do, csn, sd_cmd_timeout, sd_data_timeout);
end generate ftype1;
-- ftype2: if ftype = 2 generate
-- csn <= 'Z';
-- di <= 'Z';
-- flash0 : m25p80
-- generic map (TimingChecksOn => false,
-- MsgOn => debug = 1,
-- UserPreLoad => true)
-- port map (C => sck, D => di, SNeg => csn, HOLDNeg => vdd,
-- WNeg => vdd, Q => do);
-- end generate ftype2;
ftype3: if ftype = 3 generate
csn <= 'Z';
simple_spi_flash_model (
dbg => debug,
readcmd => readcmd,
dummybyte => dummybyte /= 0,
dualoutput => dualoutput /= 0,
sck => sck,
di => di,
do => do,
csn => csn);
end generate ftype3;
ftype4: if ftype = 4 generate
spi_memory_model (
dbg => debug,
readcmd => readcmd,
dummybyte => dummybyte /= 0,
dualoutput => dualoutput /= 0,
sck => sck,
di => di,
do => do,
csn => csn);
csn <= 'Z';
end generate ftype4;
notsupported: if ftype > 4 generate
assert false report "spi_flash: no model" severity failure;
end generate notsupported;
end sim;