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

Subversion Repositories spiflashcontroller

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /
    from Rev 1 to Rev 2
    Reverse comparison

Rev 1 → Rev 2

/trunk/spi_ctrl.vhd
0,0 → 1,548
--
-- Copyright (C) 2006 Johannes Hausensteiner (johannes.hausensteiner@pcl.at)
--
-- 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
--
--
-- Filename: spi_ctrl.vhd
--
-- Function: SPI Flash controller for DIY Calculator
--
--
-- Changelog
--
-- 0.1 25.Sep.2006 JH new
--
 
 
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
 
entity spi_ctrl is
port (
clk : in std_logic;
rst : in std_logic;
spi_clk : out std_logic;
spi_cs : out std_logic;
spi_din : in std_logic;
spi_dout : out std_logic;
sel : in std_logic;
nWR : in std_logic;
addr : in std_logic_vector (1 downto 0);
d_in : in std_logic_vector (7 downto 0);
d_out : out std_logic_vector (7 downto 0)
);
end spi_ctrl;
 
architecture rtl of spi_ctrl is
type state_t is (
IDLE, TxCMD, TxADD_H, TxADD_M, TxADD_L, TxDUMMY, TxDATA, RxDATA,
WAIT1, WAIT2, WAIT3, WAIT4, WAIT6, WAIT5, WAIT7, WAIT8, CLR_CMD);
signal state, next_state : state_t;
 
-- transmitter
signal tx_reg, tx_sreg : std_logic_vector (7 downto 0);
signal tx_empty, tx_empty_set : std_logic;
signal tx_bit_cnt : std_logic_vector (3 downto 0);
 
-- receiver
signal rx_sreg : std_logic_vector (7 downto 0);
signal rx_ready, rx_ready_set : std_logic;
signal rx_bit_cnt : std_logic_vector (3 downto 0);
 
signal wr_cmd, wr_data, wr_add_m, wr_add_l : std_logic;
signal rd_stat, rd_add_m, rd_add_l : std_logic;
signal rd_data, rd_data1, rd_data2 : std_logic;
signal spi_cs_int : std_logic;
 
-- auxiliary signals
signal rx_enable, rx_empty, rx_empty_clr : std_logic;
signal tx_enable, tx_enable_d : std_logic;
signal tx_new_data, tx_new_data_clr, is_tx_data, is_wait6 : std_logic;
signal cmd_clr, busy : std_logic;
 
-- registers
signal cmd, tx_data, rx_data, add_m, add_l : std_logic_vector (7 downto 0);
-- FLASH commands
constant NOP : std_logic_vector (7 downto 0) := x"FF"; -- no cmd to execute
constant WREN : std_logic_vector (7 downto 0) := x"06"; -- write enable
constant WRDI : std_logic_vector (7 downto 0) := x"04"; -- write disable
constant RDSR : std_logic_vector (7 downto 0) := x"05"; -- read status reg
constant WRSR : std_logic_vector (7 downto 0) := x"01"; -- write stat. reg
constant RD : std_logic_vector (7 downto 0) := x"03"; -- read data
constant F_RD : std_logic_vector (7 downto 0) := x"0B"; -- fast read data
constant PP : std_logic_vector (7 downto 0) := x"02"; -- page program
constant SE : std_logic_vector (7 downto 0) := x"D8"; -- sector erase
constant BE : std_logic_vector (7 downto 0) := x"C7"; -- bulk erase
constant DP : std_logic_vector (7 downto 0) := x"B9"; -- deep power down
constant RES : std_logic_vector (7 downto 0) := x"AB"; -- read signature
begin
-- assign signals
spi_cs <= spi_cs_int;
spi_clk <= not ((tx_enable or rx_enable) and spi_cs_int)
or rx_ready or rx_ready_set or clk;
spi_dout <= tx_sreg(7);
 
-- address decoder
process (sel, addr, nWR)
variable input : std_logic_vector (3 downto 0);
begin
input := sel & addr & nWR;
-- defaults
wr_data <= '0';
wr_cmd <= '0';
wr_add_m <= '0';
wr_add_l <= '0';
rd_data <= '0';
rd_stat <= '0';
rd_add_m <= '0';
rd_add_l <= '0';
case input is
when "1000" => wr_data <= '1';
when "1001" => rd_data <= '1';
when "1010" => wr_cmd <= '1';
when "1011" => rd_stat <= '1';
when "1100" => wr_add_m <= '1';
when "1101" => rd_add_m <= '1';
when "1110" => wr_add_l <= '1';
when "1111" => rd_add_l <= '1';
when others => null;
end case;
end process;
 
-- read back registers
d_out(0) <= (rx_data(0) and rd_data)
or (busy and rd_stat)
or (add_m(0) and rd_add_m)
or (add_l(0) and rd_add_l);
 
d_out(1) <= (rx_data(1) and rd_data)
or (tx_empty and rd_stat)
or (add_m(1) and rd_add_m)
or (add_l(1) and rd_add_l);
 
d_out(2) <= (rx_data(2) and rd_data)
or (rx_ready and rd_stat)
or (add_m(2) and rd_add_m)
or (add_l(2) and rd_add_l);
 
d_out(3) <= (rx_data(3) and rd_data)
or (is_wait6 and rd_stat)
or (add_m(3) and rd_add_m)
or (add_l(3) and rd_add_l);
 
d_out(4) <= (rx_data(4) and rd_data)
or ('0' and rd_stat)
or (add_m(4) and rd_add_m)
or (add_l(4) and rd_add_l);
 
d_out(5) <= (rx_data(5) and rd_data)
or ('0' and rd_stat)
or (add_m(5) and rd_add_m)
or (add_l(5) and rd_add_l);
 
d_out(6) <= (rx_data(6) and rd_data)
or ('0' and rd_stat)
or (add_m(6) and rd_add_m)
or (add_l(6) and rd_add_l);
 
d_out(7) <= (rx_data(7) and rd_data)
or ('0' and rd_stat)
or (add_m(7) and rd_add_m)
or (add_l(7) and rd_add_l);
 
-- write command register
process (rst, cmd_clr, wr_cmd)
begin
if rst = '1' or cmd_clr = '1' then
cmd <= NOP;
elsif falling_edge (wr_cmd) then
cmd <= d_in;
end if;
end process;
 
-- write address mid register
process (rst, wr_add_m)
begin
if rst = '1' then
add_m <= x"00";
elsif falling_edge (wr_add_m) then
add_m <= d_in;
end if;
end process;
 
-- write address low register
process (rst, wr_add_l)
begin
if rst = '1' then
add_l <= x"00";
elsif falling_edge (wr_add_l) then
add_l <= d_in;
end if;
end process;
 
-- write tx data register
process (rst, wr_data)
begin
if rst = '1' then
tx_data <= x"00";
elsif falling_edge (wr_data) then
tx_data <= d_in;
end if;
end process;
 
-- new tx data flag
tx_new_data_clr <= tx_empty_set and is_tx_data;
process (rst, tx_new_data_clr, wr_data)
begin
if rst = '1' or tx_new_data_clr = '1' then
tx_new_data <= '0';
elsif falling_edge (wr_data) then
tx_new_data <= '1';
end if;
end process;
 
-- advance the state machine
process (rst, clk)
begin
if rst = '1' then
state <= IDLE;
elsif rising_edge (clk) then
state <= next_state;
end if;
end process;
 
-- state machine transition table
process (state, cmd, tx_bit_cnt, tx_new_data, rx_bit_cnt, rx_empty)
begin
case state is
when IDLE =>
case cmd is
when NOP => next_state <= IDLE;
when others => next_state <= TxCMD;
end case;
 
when TxCMD =>
if tx_bit_cnt < x"7" then
next_state <= TxCMD;
else
next_state <= WAIT1;
end if;
 
when WAIT1 =>
case cmd is
when WREN | WRDI | BE | DP => next_state <= CLR_CMD;
when SE | PP | RES | RD | F_RD => next_state <= TxADD_H;
when WRSR => next_state <= TxDATA;
when RDSR => next_state <= RxDATA;
when others => next_state <= CLR_CMD;
end case;
 
when TxADD_H =>
if tx_bit_cnt < x"7" then
next_state <= TxADD_H;
else
next_state <= WAIT2;
end if;
 
when WAIT2 => next_state <= TxADD_M;
 
when TxADD_M =>
if tx_bit_cnt < x"7" then
next_state <= TxADD_M;
else
next_state <= WAIT3;
end if;
 
when WAIT3 => next_state <= TxADD_L;
 
when TxADD_L =>
if tx_bit_cnt < x"7" then
next_state <= TxADD_L;
else
case cmd is
when PP => next_state <= WAIT6;
when SE | RES | RD | F_RD => next_state <= WAIT4;
when others => next_state <= CLR_CMD;
end case;
end if;
 
when WAIT4 =>
case cmd is
when F_RD => next_state <= TxDUMMY;
when RES | RD => next_state <= RxDATA;
when others => next_state <= CLR_CMD;
end case;
 
when TxDUMMY =>
if tx_bit_cnt < x"7" then
next_state <= TxDUMMY;
else
next_state <= WAIT8;
end if;
 
when WAIT7 => next_state <= WAIT8;
 
when WAIT8 =>
case cmd is
when RD | F_RD =>
if rx_empty = '1' then
next_state <= RxDATA;
else
next_state <= WAIT8;
end if;
when others => next_state <= CLR_CMD;
end case;
 
when RxDATA =>
if rx_bit_cnt < x"7" then
next_state <= RxDATA;
else
case cmd is
when RD | F_RD => next_state <= WAIT7;
when RDSR | RES => next_state <= WAIT5;
when others => next_state <= CLR_CMD;
end case;
end if;
 
when TxDATA =>
if tx_bit_cnt < x"7" then
next_state <= TxDATA;
else
case cmd is
when PP => next_state <= WAIT6;
when others => next_state <= CLR_CMD;
end case;
end if;
 
when WAIT6 =>
case cmd is
when PP =>
if tx_new_data = '1' then
next_state <= TxDATA;
else
next_state <= WAIT6;
end if;
when others => next_state <= CLR_CMD;
end case;
 
when WAIT5 => next_state <= CLR_CMD;
 
when CLR_CMD => next_state <= IDLE;
end case;
end process;
 
-- state machine output table
process (state)
begin
-- default values
tx_enable <= '0';
rx_enable <= '0';
tx_reg <= x"FF";
spi_cs_int <= '0';
busy <= '1';
cmd_clr <= '0';
is_tx_data <= '0';
is_wait6 <= '0';
 
case state is
when IDLE =>
busy <= '0';
when TxCMD =>
tx_reg <= cmd;
tx_enable <= '1';
spi_cs_int <= '1';
when TxDATA =>
tx_reg <= tx_data;
tx_enable <= '1';
spi_cs_int <= '1';
is_tx_data <= '1';
when TxADD_H =>
tx_reg <= x"0F";
tx_enable <= '1';
spi_cs_int <= '1';
when TxADD_M =>
tx_reg <= add_m;
tx_enable <= '1';
spi_cs_int <= '1';
when TxADD_L =>
tx_reg <= add_l;
tx_enable <= '1';
spi_cs_int <= '1';
when TxDUMMY =>
tx_reg <= x"00";
tx_enable <= '1';
spi_cs_int <= '1';
when RxDATA =>
rx_enable <= '1';
spi_cs_int <= '1';
when WAIT1 | WAIT2 | WAIT3 | WAIT4 | WAIT8 =>
spi_cs_int <= '1';
when WAIT6 =>
is_wait6 <= '1';
spi_cs_int <= '1';
when WAIT5 | WAIT7 =>
rx_enable <= '1';
spi_cs_int <= '1';
when CLR_CMD =>
cmd_clr <= '1';
when others => null;
end case;
end process;
 
-- the tx_empty flip flop
process (rst, tx_empty_set, wr_data)
begin
if rst = '1' then
tx_empty <= '1';
elsif wr_data = '1' then
tx_empty <= '0';
elsif rising_edge (tx_empty_set) then
tx_empty <= '1';
end if;
end process;
 
-- delay the tx_enable signal
process (rst, clk)
begin
if rst = '1' then
tx_enable_d <= '0';
elsif falling_edge (clk) then
tx_enable_d <= tx_enable;
end if;
end process;
 
-- transmitter shift register and bit counter
process (rst, tx_enable_d, clk)
begin
if rst = '1' or tx_enable_d = '0' then
tx_sreg <= tx_reg;
tx_bit_cnt <= x"0";
tx_empty_set <= '0';
elsif falling_edge (clk) then
tx_bit_cnt <= tx_bit_cnt + 1;
 
tx_sreg(7) <= tx_sreg(6);
tx_sreg(6) <= tx_sreg(5);
tx_sreg(5) <= tx_sreg(4);
tx_sreg(4) <= tx_sreg(3);
tx_sreg(3) <= tx_sreg(2);
tx_sreg(2) <= tx_sreg(1);
tx_sreg(1) <= tx_sreg(0);
tx_sreg(0) <= '1';
 
if tx_bit_cnt = x"6" and is_tx_data = '1' then
tx_empty_set <= '1';
else
tx_empty_set <= '0';
end if;
end if;
end process;
 
-- capture rd_data
process (rst, rd_data, rd_data2)
begin
if rst = '1' or rd_data2 = '1' then
rd_data1 <= '0';
elsif rising_edge (rd_data) then
rd_data1 <= '1';
end if;
end process;
 
process (rst, clk)
begin
if rst = '1' then
rd_data2 <= '0';
elsif rising_edge (clk) then
rd_data2 <= rd_data1;
end if;
end process;
 
-- the rx_empty flip flop
process (rst, clk)
begin
if rst = '1' then
rx_empty <= '1';
elsif falling_edge (clk) then
if rx_empty_clr = '1' then
rx_empty <= '0';
elsif rd_data2 = '1' then
rx_empty <= '1';
end if;
end if;
end process;
 
-- the rx_ready flip flop
process (rst, clk)
begin
if rst = '1' then
rx_ready <= '0';
elsif falling_edge (clk) then
if rd_data2 = '1' then
rx_ready <= '0';
elsif rx_ready_set = '1' then
rx_ready <= '1';
end if;
end if;
end process;
 
-- the rx_data register
process (rst, clk)
begin
if rst = '1' then
rx_data <= x"FF";
elsif falling_edge (clk) then
if rx_ready_set = '1' then
rx_data <= rx_sreg;
end if;
end if;
end process;
 
-- receiver shift register and bit counter
process (rst, rx_enable, clk)
begin
if rst = '1' or rx_enable = '0' then
rx_bit_cnt <= x"0";
rx_ready_set <= '0';
rx_empty_clr <= '0';
rx_sreg <= x"FF";
elsif rising_edge (clk) then
rx_bit_cnt <= rx_bit_cnt + 1;
 
rx_sreg(7) <= rx_sreg(6);
rx_sreg(6) <= rx_sreg(5);
rx_sreg(5) <= rx_sreg(4);
rx_sreg(4) <= rx_sreg(3);
rx_sreg(3) <= rx_sreg(2);
rx_sreg(2) <= rx_sreg(1);
rx_sreg(1) <= rx_sreg(0);
rx_sreg(0) <= spi_din;
 
if rx_bit_cnt = x"1" then
rx_empty_clr <= '1';
else
rx_empty_clr <= '0';
end if;
 
if rx_bit_cnt = x"7" then
rx_ready_set <= '1';
else
rx_ready_set <= '0';
end if;
end if;
end process;
end rtl;
/trunk/tb_spi_ctrl.vhd
0,0 → 1,396
 
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
 
entity test_spi_ctrl is
end test_spi_ctrl;
 
architecture test of test_spi_ctrl is
signal rst, clk, sel, nRD, nWR : std_logic;
signal addr : std_logic_vector (1 downto 0);
signal spi_clk, spi_cs, spi_din, spi_dout : std_logic;
signal d_in, d_out, stat, data : std_logic_vector (7 downto 0);
-- FLASH commands
constant NOP : std_logic_vector (7 downto 0) := x"FF"; -- no cmd to execute
constant WREN : std_logic_vector (7 downto 0) := x"06"; -- write enable
constant WRDI : std_logic_vector (7 downto 0) := x"04"; -- write disable
constant RDSR : std_logic_vector (7 downto 0) := x"05"; -- read status reg
constant WRSR : std_logic_vector (7 downto 0) := x"01"; -- write stat. reg
constant RD : std_logic_vector (7 downto 0) := x"03"; -- read data
constant F_RD : std_logic_vector (7 downto 0) := x"0B"; -- fast read data
constant PP : std_logic_vector (7 downto 0) := x"02"; -- page program
constant SE : std_logic_vector (7 downto 0) := x"D8"; -- sector erase
constant BE : std_logic_vector (7 downto 0) := x"C7"; -- bulk erase
constant DP : std_logic_vector (7 downto 0) := x"B9"; -- deep power down
constant RES : std_logic_vector (7 downto 0) := x"AB"; -- read signature
-- status register bit masks
constant STAT_BUSY : std_logic_vector (7 downto 0) := x"01";
constant STAT_TXE : std_logic_vector (7 downto 0) := x"02";
constant STAT_RXR : std_logic_vector (7 downto 0) := x"04";
constant STAT_WDAT : std_logic_vector (7 downto 0) := x"08";
begin
dut : entity work.spi_ctrl port map (
rst => rst,
clk => clk,
spi_clk => spi_clk,
spi_cs => spi_cs,
spi_din => spi_din,
spi_dout => spi_dout,
sel => sel,
nWR => nWR,
nRD => nRD,
addr => addr,
d_in => d_in,
d_out => d_out
);
 
process is
begin
clk <= '0'; wait for 80 ns;
clk <= '1'; wait for 80 ns;
end process;
process is
begin
rst <= '0'; wait for 50 ns;
rst <= '1'; wait for 120 ns;
rst <= '0';
wait;
end process;
 
process
begin
-- initial condition
sel <= '0'; addr <= "00"; nRD <= '1'; nWR <= '1'; d_in <= x"FF";
wait for 1420 ns;
 
-- write command WREN
sel <= '1'; addr <= "01"; d_in <= WREN; wait for 5 ns;
nWR <= '0'; wait for 100 ns;
nWR <= '1'; wait for 5 ns;
sel <= '0'; d_in <= x"FF"; wait for 2 us;
 
-- write command WRDI
sel <= '1'; addr <= "01"; d_in <= WRDI; wait for 5 ns;
nWR <= '0'; wait for 100 ns;
nWR <= '1'; wait for 5 ns;
sel <= '0'; d_in <= x"FF"; wait for 2 us;
 
-- write command WRSR: data
sel <= '1'; addr <= "00"; d_in <= x"55"; wait for 5 ns;
nWR <= '0'; wait for 100 ns;
nWR <= '1'; wait for 5 ns;
sel <= '0'; d_in <= x"FF"; wait for 10 ns;
-- the command
sel <= '1'; addr <= "01"; d_in <= WRSR; wait for 5 ns;
nWR <= '0'; wait for 100 ns;
nWR <= '1'; wait for 5 ns;
sel <= '0'; d_in <= x"FF"; wait for 4 us;
 
-- write command SE: address mid
sel <= '1'; addr <= "10"; d_in <= x"AB"; wait for 5 ns;
nWR <= '0'; wait for 100 ns;
nWR <= '1'; wait for 5 ns;
sel <= '0'; d_in <= x"FF"; wait for 10 ns;
-- address low
sel <= '1'; addr <= "11"; d_in <= x"CD"; wait for 5 ns;
nWR <= '0'; wait for 100 ns;
nWR <= '1'; wait for 5 ns;
sel <= '0'; d_in <= x"FF"; wait for 10 ns;
-- the command
sel <= '1'; addr <= "01"; d_in <= SE; wait for 5 ns;
nWR <= '0'; wait for 100 ns;
nWR <= '1'; wait for 5 ns;
sel <= '0'; d_in <= x"FF"; wait for 6.5 us;
 
-- write command PP: address mid
sel <= '1'; addr <= "10"; d_in <= x"67"; wait for 5 ns;
nWR <= '0'; wait for 100 ns;
nWR <= '1'; wait for 5 ns;
sel <= '0'; d_in <= x"FF"; wait for 10 ns;
-- address low
sel <= '1'; addr <= "11"; d_in <= x"89"; wait for 5 ns;
nWR <= '0'; wait for 100 ns;
nWR <= '1'; wait for 5 ns;
sel <= '0'; d_in <= x"FF"; wait for 10 ns;
-- the command
sel <= '1'; addr <= "01"; d_in <= PP; wait for 5 ns;
nWR <= '0'; wait for 100 ns;
nWR <= '1'; wait for 5 ns;
sel <= '0'; d_in <= x"FF"; wait for 100 ns;
-- some data
for i in 0 to 20 loop
-- wait for tx_empty
stat <= x"00"; wait for 10 ns;
while (stat and STAT_WDAT) /= STAT_WDAT loop
sel <= '1'; addr <= "01"; wait for 5 ns;
nRD <= '0'; wait for 100 ns;
stat <= d_out; nRD <= '1'; wait for 5 ns;
sel <= '0'; wait for 1 us;
end loop;
-- write new data
sel <= '1'; addr <= "00";
d_in <= std_logic_vector(TO_UNSIGNED(i, d_in'Length)); wait for 5 ns;
nWR <= '0'; wait for 100 ns;
nWR <= '1'; wait for 5 ns;
sel <= '0'; d_in <= x"FF"; wait for 1 us;
end loop;
-- send one more byte
wait for 10 us;
sel <= '1'; addr <= "00"; d_in <= x"AA"; wait for 5 ns;
nWR <= '0'; wait for 100 ns;
nWR <= '1'; wait for 5 ns;
sel <= '0'; d_in <= x"FF"; wait for 1 us;
-- write the NOP command to terminate
sel <= '1'; addr <= "01"; d_in <= NOP; wait for 5 ns;
nWR <= '0'; wait for 100 ns;
nWR <= '1'; wait for 5 ns;
sel <= '0'; d_in <= x"FF"; wait for 100 ns;
 
wait for 40 us;
 
-- now receive something, cmd RDSR
sel <= '1'; addr <= "01"; d_in <= RDSR; wait for 5 ns;
nWR <= '0'; wait for 100 ns;
nWR <= '1'; wait for 5 ns;
sel <= '0'; d_in <= x"FF";
-- poll for rx_ready
stat <= x"00"; wait for 10 ns;
while (stat and STAT_RXR) /= STAT_RXR loop
wait for 200 ns;
sel <= '1'; addr <= "01"; wait for 5 ns;
nRD <= '0'; wait for 100 ns;
stat <= d_out; nRD <= '1'; wait for 5 ns;
sel <= '0';
end loop;
wait for 100 ns;
-- read the data
sel <= '1'; addr <= "00"; wait for 5 ns;
nRD <= '0'; wait for 100 ns;
data <= d_out; nRD <= '1'; wait for 5 ns;
sel <= '0'; wait for 1.5 us;
 
-- RES command
sel <= '1'; addr <= "01"; d_in <= RES; wait for 5 ns;
nWR <= '0'; wait for 100 ns;
nWR <= '1'; wait for 5 ns;
sel <= '0'; d_in <= x"FF";
-- poll for rx_ready
stat <= x"00"; wait for 10 ns;
while (stat and STAT_RXR) /= STAT_RXR loop
wait for 200 ns;
sel <= '1'; addr <= "01"; wait for 5 ns;
nRD <= '0'; wait for 100 ns;
stat <= d_out; nRD <= '1'; wait for 5 ns;
sel <= '0';
end loop;
wait for 100 ns;
-- read the data
sel <= '1'; addr <= "00"; wait for 5 ns;
nRD <= '0'; wait for 100 ns;
data <= d_out; nRD <= '1'; wait for 5 ns;
sel <= '0'; wait for 1.5 us;
 
-- READ command
sel <= '1'; addr <= "10"; d_in <= x"12"; wait for 5 ns;
nWR <= '0'; wait for 100 ns;
nWR <= '1'; wait for 5 ns;
sel <= '0'; d_in <= x"FF"; wait for 10 ns;
-- address low
sel <= '1'; addr <= "11"; d_in <= x"34"; wait for 5 ns;
nWR <= '0'; wait for 100 ns;
nWR <= '1'; wait for 5 ns;
sel <= '0'; d_in <= x"FF"; wait for 10 ns;
-- the command
sel <= '1'; addr <= "01"; d_in <= RD; wait for 5 ns;
nWR <= '0'; wait for 100 ns;
nWR <= '1'; wait for 5 ns;
sel <= '0'; d_in <= x"FF";
-- read data
for i in 1 to 10 loop
-- poll for rx_ready
stat <= x"00"; wait for 10 ns;
while (stat and STAT_RXR) /= STAT_RXR loop
wait for 200 ns;
sel <= '1'; addr <= "01"; wait for 5 ns;
nRD <= '0'; wait for 100 ns;
stat <= d_out; nRD <= '1'; wait for 5 ns;
sel <= '0';
end loop;
wait for 100 ns;
-- read the data
sel <= '1'; addr <= "00"; wait for 5 ns;
nRD <= '0'; wait for 100 ns;
data <= d_out; nRD <= '1'; wait for 5 ns;
sel <= '0';
end loop;
wait for 1 us;
-- write the NOP command to terminate
sel <= '1'; addr <= "01"; d_in <= NOP; wait for 5 ns;
nWR <= '0'; wait for 100 ns;
nWR <= '1'; wait for 5 ns;
sel <= '0'; d_in <= x"FF";
 
wait;
end process;
 
process
begin
spi_din <= '1'; wait for 144.8 us;
 
-- input data for RDSR cmd 0x54
spi_din <= '0'; wait for 160 ns;
spi_din <= '1'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '1'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '1'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
 
spi_din <= '1'; wait for 8 us;
 
-------------------------------
 
-- input data for RES cmd 0xAB
spi_din <= '1'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '1'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '1'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '1'; wait for 160 ns;
spi_din <= '1'; wait for 160 ns;
 
spi_din <= '1'; wait for 8.160 us;
 
-------------------------------
 
-- input data for RD cmd 0x01
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '1'; wait for 160 ns;
 
spi_din <= '1'; wait for 640 ns;
 
-- input data for RD cmd 0x02
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '1'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
 
spi_din <= '1'; wait for 480 ns;
 
-- input data for RD cmd 0x03
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '1'; wait for 160 ns;
spi_din <= '1'; wait for 160 ns;
 
spi_din <= '1'; wait for 480 ns;
 
-- input data for RD cmd 0x04
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '1'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
 
spi_din <= '1'; wait for 640 ns;
 
-- input data for RD cmd 0x05
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '1'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '1'; wait for 160 ns;
 
spi_din <= '1'; wait for 800 ns;
 
-- input data for RD cmd 0x06
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '1'; wait for 160 ns;
spi_din <= '1'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
 
spi_din <= '1'; wait for 800 ns;
 
-- input data for RD cmd 0x07
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '1'; wait for 160 ns;
spi_din <= '1'; wait for 160 ns;
spi_din <= '1'; wait for 160 ns;
 
spi_din <= '1'; wait for 800 ns;
 
-- input data for RD cmd 0x08
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '1'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
 
spi_din <= '1'; wait for 800 ns;
 
-- input data for RD cmd 0x09
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '1'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '1'; wait for 160 ns;
 
spi_din <= '1'; wait for 800 ns;
 
-- input data for RD cmd 0x0A
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '1'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
spi_din <= '1'; wait for 160 ns;
spi_din <= '0'; wait for 160 ns;
 
spi_din <= '1';
 
wait;
end process;
end test;
 
 
 
/trunk/doc/SPI_state-diagram.jpg Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream
trunk/doc/SPI_state-diagram.jpg Property changes : Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Index: trunk/doc/spi-registers.txt =================================================================== --- trunk/doc/spi-registers.txt (nonexistent) +++ trunk/doc/spi-registers.txt (revision 2) @@ -0,0 +1,181 @@ +Description of the internal SPI Flash controller: +================================================= + +The SPI Flash controller occupies the following addresses in the +DIY calculator address space: + $F038 : Tx data register (write) + $F018 : Rx data register (read) + $F039 : command register (write) + $F019 : status register (read) + $F03A : address mid register (write) + $F03B : address low register (write) + +The high address byte is fixed to $0F, denoting the last segment within +the flash (8MBit type assumed). + + +These are the bits of the status register: + busy: $01 + tx empty: $02 + rx ready: $04 +Other bits read as '0'. + + +How to communicate with the SPI flash: +-------------------------------------- + +The SPI flash (ST M25P80 chip) understands the following commands: + WREN ($06) .. write enable + WRDI ($04) .. write disable + RDSR ($05) .. read status register + WRSR ($01) .. write status register + RD ($03) .. read data + F_RD ($0B) .. fast read data + PP ($02) .. page program + SE ($D8) .. sector erase + BE ($C7) .. bulk erase + DP ($B9) .. deep power down + RES ($AB) .. read signature + +Additionally there is a pseudo-command defined for use with the SPI flash +controller: + NOP ($FF) .. no cmd to execute/end current command + + +Command classification: +----------------------- + + Write Enable (WREN) transmit 1 byte ... cmd (0x06) + Write Disable (WRDI) (0x04) + Bulk Erase (BE) (0xC7) + Deep Power Down (DP) (0xB9) + + Write Status reg (WRSR) transmit 1 byte ... cmd (0x01) + 1 byte ... SR contents + + Sector Erase (SE) transmit 1 byte ... cmd (0xD8) + 3 bytes .. address + + Page Program (PP) transmit 1 byte ... cmd (0x02) + 3 bytes .. address + 1-256 bytes .. data + + Read Status reg (RDSR) transmit 1 byte ... cmd (0x05) + receive 1 byte ... SR contents + + Read Signature (RES) transmit 1 byte ... cmd (0xAB) + 3 bytes .. dummy + receive 1 byte ... the signature (0x13) + + Read Data (RD) transmit 1 byte ... cmd (0x03) + 3 bytes .. address + receive n bytes .. data + + Fast Read Data (F_RD) transmit 1 byte ... cmd (0x0B) + 3 bytes .. address + 1 byte ... dummy + receive n bytes .. data + + +A command sequence depends on the command to be executed. For the simple +commands (with no parameters) just the command is written to the SPI Flash +controller command register (address $F039). The SPI controller shifts the +cmd byte into the SPI flash. The more complex commands (with parameters) +require that the parameters (e.g. address) are written first. The action +of writing the command register triggers the transmission of the command +plus all necessary parameter bytes to the SPI flash chip. With commands +that receive a response (read commands) you have to wait for the response +to arrive and then read the SPI flash controller data register ($F018). + + +Examples +-------- + +1) issue the "Write Enable" command: + LDA SPI_WREN + STA [SPI_CMD] + +2) issue the "Read Signature" command: + LDA SPI_RES + STA [SPI_CMD] +; wait for the response to arrive +WAIT: LDA [SPI_STAT] + AND SPI_RXR + JZ [WAIT] +; read the response + LDA [SPI_RX] + +3) issue the "Sector Erase" command: + LDA 0 + STA [SPI_AHI] + STA [SPI_ALO] + LDA SPI_SE + STA [SPI_CMD] + +4) issue the "Read Data" command: +; symbolic constant +BUFSIZE: .EQU 100 + . + . + . + +; buffer reservation, in RAM +BUF: .BLOCK BUFSIZE +MAXBYTES: .WORD +NUMBYTES: .WORD + . + . + . + +; code: + BLDX BUFSIZE + BSTX [MAXBYTES] + LDA BUF + STA [SPI_AHI] + LDA BUF+1 + STA [SPI_ALO] + LDA SPI_RD + STA [SPI_CMD] + BLDX 0 + BSTX [NUMBYTES] +; now wait for the data to arrive +LOOP: LDA [SPI_STAT] + AND SPI_RXR + JZ [LOOP] +; data byte is ready + LDA [SPI_RX] + STA [BUF, X] + INCX +; check for max number of bytes reached, high byte first + BSTX [NUMBYTES] + LDA [MAXBYTES] + CMPA [NUMBYTES] + JC [LOOP] ; not yet reached + JNZ [DONE] +; high bytes are equal => compare low bytes as well + LDA [MAXBYTES+1] + CMP [NUMBYTES+1] + JC [LOOP] ; not yet reached +; we are done now, transfer of desired number of bytes is completed +DONE: LDA SPI_NOP + STA [SPI_CMD] ; write the pseudo "NOP" cmd to reset + ; the SPI controller to idle state + +Remarks: +-------- +The flash utilizes a 3 byte address (using 20 bits for the 8Mbit +M25P80 chip). The highest byte specifies the sector on which the PP, SE, +RD, F_RD commands operate. Currently we use only the topmost sector +(no. 0x0F). This is no restriction in size of memory since one sector +is 64KB in size. But it saves the additional high byte address register. +The high byte (0x0F) is hardwired inside the SPI Flash controller. + +The PP, RD, and F_RD commands are special in that that the number of bytes +to be transferred is not known in advance. Therefore the dummy "NOP" command +must be issued to the SPI Flash controller after all bytes are transferred +to terminate the active command and return the SPI Flash controller to its +idle state. + +For commands that expect a response (read commands) it is necessary to poll +the SPI controller status register (address $F019) to see when the data has +arrived.

powered by: WebSVN 2.1.0

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