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.