URL
https://opencores.org/ocsvn/spiflashcontroller/spiflashcontroller/trunk
Subversion Repositories spiflashcontroller
Compare Revisions
- This comparison shows the changes necessary to convert path
/
- from Rev 6 to Rev 7
- ↔ Reverse comparison
Rev 6 → Rev 7
/tags/V01/spi_ctrl.vhd
File deleted
/tags/V01/tb_spi_ctrl.vhd
File deleted
/tags/V01/doc/SPI_state-diagram.jpg
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
tags/V01/doc/SPI_state-diagram.jpg
Property changes :
Deleted: svn:mime-type
## -1 +0,0 ##
-application/octet-stream
\ No newline at end of property
Index: tags/V01/doc/spi-registers.txt
===================================================================
--- tags/V01/doc/spi-registers.txt (revision 6)
+++ tags/V01/doc/spi-registers.txt (nonexistent)
@@ -1,181 +0,0 @@
-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.
Index: trunk/spi_ctrl.vhd
===================================================================
--- trunk/spi_ctrl.vhd (revision 6)
+++ trunk/spi_ctrl.vhd (nonexistent)
@@ -1,630 +0,0 @@
---
--- 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
--- 0.2 15.Nov.2006 JH remove old code
--- 1.0 5.Feb.2007 JH new clocking scheme
--- 1.1 4.Apr.2007 JH implement high address byte
--- 1.2 16.Apr.2007 JH clock enable
--- 1.3 23.Apr.2007 JH remove all asynchronous elements
--- 1.4 4.May 2007 JH resolve read timing
--- 1.5 10.May 2007 JH remove read signal
---
-
-
-library ieee;
-use ieee.std_logic_1164.all;
-use ieee.std_logic_unsigned.all;
-
-entity spi_ctrl is
- port (
- clk_in : 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;
- wr : in std_logic;
- addr : in std_logic_vector (2 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
- -- clock generator
- constant SYS_FREQ : integer := 25000000; -- 25MHz
- constant SPI_FREQ : integer := 6250000; -- 6.25MHz
- signal clk_en : std_logic;
- signal clk_cnt : integer range 0 to (SYS_FREQ/SPI_FREQ)-1;
-
- 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, rx_bit_cnt_clr : std_logic;
- signal rx_bit_cnt : std_logic_vector (3 downto 0);
-
- signal wr_cmd, wr_data, wr_add_h, wr_add_m, wr_add_l : std_logic;
- signal rd_stat, rd_add_h, rd_add_m, rd_add_l : std_logic;
- signal rd_data, rd_data1, rd_data2 : std_logic;
- signal spi_cs_int, spi_clk_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 : std_logic_vector (7 downto 0);
- signal add_h, 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 RDCMD: 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 <= spi_clk_int;
- spi_dout <= tx_sreg(7);
-
- -- clock generator
- spi_divider : process (rst, clk_in)
- begin
- if rst = '1' then
- clk_cnt <= 0;
- clk_en <= '0';
- spi_clk_int <= '1';
- elsif falling_edge (clk_in) then
- if clk_cnt = ((SYS_FREQ / SPI_FREQ) - 2) or
- clk_cnt = ((SYS_FREQ / SPI_FREQ) - 3) then
- clk_cnt <= clk_cnt + 1;
- clk_en <= '0';
- if tx_enable = '1' or rx_enable = '1' then
- spi_clk_int <= '0';
- else
- spi_clk_int <= '1';
- end if;
- elsif clk_cnt = ((SYS_FREQ / SPI_FREQ) - 1) then
- clk_cnt <= 0;
- clk_en <= '1';
- spi_clk_int <= '1';
- else
- clk_cnt <= clk_cnt + 1;
- clk_en <= '0';
- spi_clk_int <= '1';
- end if;
- end if;
- end process;
-
- -- address decoder
- process (sel, addr, wr)
- variable input : std_logic_vector (4 downto 0);
- begin
- input := sel & addr & wr;
- -- defaults
- wr_data <= '0';
- wr_cmd <= '0';
- wr_add_h <= '0';
- wr_add_m <= '0';
- wr_add_l <= '0';
- rd_data <= '0';
- rd_stat <= '0';
- rd_add_h <= '0';
- rd_add_m <= '0';
- rd_add_l <= '0';
- case input is
- when "10000" => rd_data <= '1';
- when "10001" => wr_data <= '1';
- when "10010" => rd_stat <= '1';
- when "10011" => wr_cmd <= '1';
- when "10100" => rd_add_l <= '1';
- when "10101" => wr_add_l <= '1';
- when "10110" => rd_add_m <= '1';
- when "10111" => wr_add_m <= '1';
- when "11000" => rd_add_h <= '1';
- when "11001" => wr_add_h <= '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_h(0) and rd_add_h)
- 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_h(1) and rd_add_h)
- 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_h(2) and rd_add_h)
- 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_h(3) and rd_add_h)
- 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_h(4) and rd_add_h)
- 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_h(5) and rd_add_h)
- 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_h(6) and rd_add_h)
- 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_h(7) and rd_add_h)
- or (add_m(7) and rd_add_m)
- or (add_l(7) and rd_add_l);
-
- -- write command register
- process (rst, cmd_clr, clk_in)
- begin
- if rst = '1' or cmd_clr = '1' then
- cmd <= NOP;
- elsif rising_edge (clk_in) then
- if wr_cmd = '1' then
- cmd <= d_in;
- end if;
- end if;
- end process;
-
- -- write address high register
- process (rst, clk_in)
- begin
- if rst = '1' then
- add_h <= x"00";
- elsif rising_edge (clk_in) then
- if wr_add_h = '1' then
- add_h <= d_in;
- end if;
- end if;
- end process;
-
- -- write address mid register
- process (rst, clk_in)
- begin
- if rst = '1' then
- add_m <= x"00";
- elsif rising_edge (clk_in) then
- if wr_add_m ='1' then
- add_m <= d_in;
- end if;
- end if;
- end process;
-
- -- write address low register
- process (rst, clk_in)
- begin
- if rst = '1' then
- add_l <= x"00";
- elsif rising_edge (clk_in) then
- if wr_add_l ='1' then
- add_l <= d_in;
- end if;
- end if;
- end process;
-
- -- write tx data register
- process (rst, clk_in)
- begin
- if rst = '1' then
- tx_data <= x"00";
- elsif rising_edge (clk_in) then
- if wr_data = '1' then
- tx_data <= d_in;
- end if;
- 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, clk_in)
- begin
- if rst = '1' or tx_new_data_clr = '1' then
- tx_new_data <= '0';
- elsif rising_edge (clk_in) then
- if wr_data ='1' then
- tx_new_data <= '1';
- end if;
- end if;
- end process;
-
- -- advance the state machine
- process (rst, clk_in)
- begin
- if rst = '1' then
- state <= IDLE;
- elsif rising_edge (clk_in) then
- if clk_en = '1' then
- state <= next_state;
- end if;
- 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
- case cmd is
- when WREN | WRDI | BE | DP => next_state <= CLR_CMD;
- when SE | PP | RES | RDCMD | F_RD|WRSR|RDSR => next_state <= WAIT1;
- when others => next_state <= CLR_CMD;
- end case;
- end if;
-
- when WAIT1 =>
- case cmd is
- when WREN | WRDI | BE | DP => next_state <= CLR_CMD;
- when SE | PP | RES | RDCMD | 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 | RDCMD | 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 | RDCMD => 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 RDCMD | 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 RDCMD | 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, cmd, tx_data, add_m, add_l, add_h)
- begin
- -- default values
- tx_enable <= '0';
- rx_enable <= '0';
- rx_bit_cnt_clr <= '1';
- 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 <= add_h;
- 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_bit_cnt_clr <= '0';
- 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_bit_cnt_clr <= '0';
- spi_cs_int <= '1';
- when CLR_CMD =>
- cmd_clr <= '1';
- when others => null;
- end case;
- end process;
-
- -- the tx_empty flip flop
- process (rst, wr_data, clk_in)
- begin
- if rst = '1' then
- tx_empty <= '1';
- elsif wr_data = '1' then
- tx_empty <= '0';
- elsif rising_edge (clk_in) then
- if tx_empty_set = '1' then
- tx_empty <= '1';
- end if;
- end if;
- end process;
-
- -- delay the tx_enable signal
- process (rst, clk_in)
- begin
- if rst = '1' then
- tx_enable_d <= '0';
- elsif rising_edge (clk_in) then
- tx_enable_d <= tx_enable;
- end if;
- end process;
-
- -- transmitter shift register and bit counter
- process (rst, tx_reg, tx_enable_d, clk_in)
- begin
- if rst = '1' then
- tx_sreg <= x"FF";
- tx_bit_cnt <= x"0";
- tx_empty_set <= '0';
- elsif tx_enable_d = '0' then
- tx_sreg <= tx_reg;
- tx_bit_cnt <= x"0";
- tx_empty_set <= '0';
- elsif rising_edge (clk_in) then
- if clk_en = '1' then
- tx_bit_cnt <= tx_bit_cnt + 1;
- tx_sreg <= tx_sreg (6 downto 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 if;
- end process;
-
- -- synchronize rd_data
- process (rst, clk_in)
- begin
- if rst = '1' then
- rd_data1 <= '0';
- elsif falling_edge (clk_in) then
- rd_data1 <= rd_data;
- end if;
- end process;
-
- process (rst, clk_in)
- begin
- if rst = '1' then
- rd_data2 <= '0';
- elsif falling_edge (clk_in) then
- if rd_data = '0' then
- rd_data2 <= rd_data1;
- end if;
- end if;
- end process;
-
- -- the rx_empty flip flop
- process (rst, clk_in)
- begin
- if rst = '1' then
- rx_empty <= '1';
- elsif rising_edge (clk_in) 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_in)
- begin
- if rst = '1' then
- rx_ready <= '0';
- elsif rising_edge (clk_in) then
- if rd_data = '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_in)
- begin
- if rst = '1' then
- rx_data <= x"FF";
- elsif rising_edge (clk_in) 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_bit_cnt_clr, clk_in)
- begin
- if rst = '1' or rx_bit_cnt_clr = '1' then
- rx_bit_cnt <= x"0";
- rx_ready_set <= '0';
- rx_empty_clr <= '0';
- rx_sreg <= x"FF";
- elsif rising_edge (clk_in) then
- if clk_en = '1' then
- rx_sreg <= rx_sreg (6 downto 0) & spi_din;
- case rx_bit_cnt is
- when x"0" =>
- rx_bit_cnt <= rx_bit_cnt + 1;
- rx_ready_set <= '0';
- rx_empty_clr <= '1';
- when x"1" | x"2" | x"3" | x"4" | x"5" | x"6" =>
- rx_bit_cnt <= rx_bit_cnt + 1;
- rx_ready_set <= '0';
- rx_empty_clr <= '0';
- when x"7" =>
- rx_bit_cnt <= rx_bit_cnt + 1;
- rx_ready_set <= '1';
- rx_empty_clr <= '0';
- when others =>
- null;
- end case;
- end if;
- end if;
- end process;
-end rtl;
Index: trunk/tb_spi_ctrl.vhd
===================================================================
--- trunk/tb_spi_ctrl.vhd (revision 6)
+++ trunk/tb_spi_ctrl.vhd (nonexistent)
@@ -1,410 +0,0 @@
-
-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, rd, wr : std_logic;
- signal addr : std_logic_vector (2 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 RDCMD: 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 (
- clk_in => clk,
- rst => rst,
- spi_clk => spi_clk,
- spi_cs => spi_cs,
- spi_din => spi_din,
- spi_dout => spi_dout,
- sel => sel,
- wr => wr,
- addr => addr,
- d_in => d_in,
- d_out => d_out
- );
-
- process is
- begin
- clk <= '0'; wait for 20 ns;
- clk <= '1'; wait for 20 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 <= "000"; rd <= '0'; wr <= '0'; d_in <= x"FF";
- wait for 1420 ns;
-
- -- write command WREN
- sel <= '1'; addr <= "001"; d_in <= WREN; wait for 5 ns;
- wr <= '1'; wait for 100 ns;
- wr <= '0'; wait for 5 ns;
- sel <= '0'; d_in <= x"FF"; wait for 2 us;
-
- -- write command WRDI
- sel <= '1'; addr <= "001"; d_in <= WRDI; wait for 5 ns;
- wr <= '1'; wait for 100 ns;
- wr <= '0'; wait for 5 ns;
- sel <= '0'; d_in <= x"FF"; wait for 2 us;
-
- -- write command WRSR: data
- sel <= '1'; addr <= "000"; d_in <= x"55"; wait for 5 ns;
- wr <= '1'; wait for 100 ns;
- wr <= '0'; wait for 5 ns;
- sel <= '0'; d_in <= x"FF"; wait for 10 ns;
- -- the command
- sel <= '1'; addr <= "001"; d_in <= WRSR; wait for 5 ns;
- wr <= '1'; wait for 100 ns;
- wr <= '0'; wait for 5 ns;
- sel <= '0'; d_in <= x"FF"; wait for 4 us;
-
- -- write command SE:
- -- address low
- sel <= '1'; addr <= "010"; d_in <= x"AB"; wait for 5 ns;
- wr <= '1'; wait for 100 ns;
- wr <= '0'; wait for 5 ns;
- sel <= '0'; d_in <= x"FF"; wait for 10 ns;
- --address mid
- sel <= '1'; addr <= "011"; d_in <= x"CD"; wait for 5 ns;
- wr <= '1'; wait for 100 ns;
- wr <= '0'; wait for 5 ns;
- sel <= '0'; d_in <= x"FF"; wait for 10 ns;
- -- address high
- sel <= '1'; addr <= "100"; d_in <= x"EF"; wait for 5 ns;
- wr <= '1'; wait for 100 ns;
- wr <= '0'; wait for 5 ns;
- sel <= '0'; d_in <= x"FF"; wait for 10 ns;
- -- the command
- sel <= '1'; addr <= "001"; d_in <= SE; wait for 5 ns;
- wr <= '1'; wait for 100 ns;
- wr <= '0'; wait for 5 ns;
- sel <= '0'; d_in <= x"FF"; wait for 6.5 us;
-
- -- write command PP:
- -- address low
- sel <= '1'; addr <= "010"; d_in <= x"45"; wait for 5 ns;
- wr <= '1'; wait for 100 ns;
- wr <= '0'; wait for 5 ns;
- sel <= '0'; d_in <= x"FF"; wait for 10 ns;
- -- address mid
- sel <= '1'; addr <= "011"; d_in <= x"67"; wait for 5 ns;
- wr <= '1'; wait for 100 ns;
- wr <= '0'; wait for 5 ns;
- sel <= '0'; d_in <= x"FF"; wait for 10 ns;
- -- address high
- sel <= '1'; addr <= "100"; d_in <= x"89"; wait for 5 ns;
- wr <= '1'; wait for 100 ns;
- wr <= '0'; wait for 5 ns;
- sel <= '0'; d_in <= x"FF"; wait for 10 ns;
- -- the command
- sel <= '1'; addr <= "001"; d_in <= PP; wait for 5 ns;
- wr <= '1'; wait for 100 ns;
- wr <= '0'; 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 <= "001"; wait for 5 ns;
- rd <= '1'; wait for 100 ns;
- stat <= d_out; rd <= '0'; wait for 5 ns;
- sel <= '0'; wait for 1 us;
- end loop;
- -- write new data
- sel <= '1'; addr <= "000";
- d_in <= std_logic_vector(TO_UNSIGNED(i, d_in'Length)); wait for 5 ns;
- wr <= '1'; wait for 100 ns;
- wr <= '0'; 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 <= "000"; d_in <= x"AA"; wait for 5 ns;
- wr <= '1'; wait for 100 ns;
- wr <= '0'; wait for 5 ns;
- sel <= '0'; d_in <= x"FF"; wait for 1 us;
- -- write the NOP command to terminate
- sel <= '1'; addr <= "001"; d_in <= NOP; wait for 5 ns;
- wr <= '1'; wait for 100 ns;
- wr <= '0'; 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 <= "001"; d_in <= RDSR; wait for 5 ns;
- wr <= '1'; wait for 100 ns;
- wr <= '0'; 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 <= "001"; wait for 5 ns;
- rd <= '1'; wait for 100 ns;
- stat <= d_out; rd <= '0'; wait for 5 ns;
- sel <= '0';
- end loop;
- wait for 100 ns;
- -- read the data
- sel <= '1'; addr <= "000"; wait for 5 ns;
- rd <= '1'; wait for 100 ns;
- data <= d_out; rd <= '0'; wait for 5 ns;
- sel <= '0'; wait for 1.5 us;
-
- -- RES command
- sel <= '1'; addr <= "001"; d_in <= RES; wait for 5 ns;
- wr <= '1'; wait for 100 ns;
- wr <= '0'; 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 <= "001"; wait for 5 ns;
- rd <= '1'; wait for 100 ns;
- stat <= d_out; rd <= '0'; wait for 5 ns;
- sel <= '0';
- end loop;
- wait for 100 ns;
- -- read the data
- sel <= '1'; addr <= "000"; wait for 5 ns;
- rd <= '1'; wait for 100 ns;
- data <= d_out; rd <= '0'; wait for 5 ns;
- sel <= '0'; wait for 1.5 us;
-
- -- READ command
- -- address low
- sel <= '1'; addr <= "010"; d_in <= x"12"; wait for 5 ns;
- wr <= '1'; wait for 100 ns;
- wr <= '0'; wait for 5 ns;
- sel <= '0'; d_in <= x"FF"; wait for 10 ns;
- -- address mid
- sel <= '1'; addr <= "011"; d_in <= x"34"; wait for 5 ns;
- wr <= '1'; wait for 100 ns;
- wr <= '0'; wait for 5 ns;
- sel <= '0'; d_in <= x"FF"; wait for 10 ns;
- -- address high
- sel <= '1'; addr <= "100"; d_in <= x"56"; wait for 5 ns;
- wr <= '1'; wait for 100 ns;
- wr <= '0'; wait for 5 ns;
- sel <= '0'; d_in <= x"FF"; wait for 10 ns;
- -- the command
- sel <= '1'; addr <= "001"; d_in <= RDCMD; wait for 5 ns;
- wr <= '1'; wait for 100 ns;
- wr <= '0'; 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 <= "001"; wait for 5 ns;
- rd <= '1'; wait for 100 ns;
- stat <= d_out; rd <= '0'; wait for 5 ns;
- sel <= '0';
- end loop;
- wait for 100 ns;
- -- read the data
- sel <= '1'; addr <= "000"; wait for 5 ns;
- rd <= '1'; wait for 100 ns;
- data <= d_out; rd <= '0'; wait for 5 ns;
- sel <= '0';
- end loop;
- wait for 1 us;
- -- write the NOP command to terminate
- sel <= '1'; addr <= "001"; d_in <= NOP; wait for 5 ns;
- wr <= '1'; wait for 100 ns;
- wr <= '0'; wait for 5 ns;
- sel <= '0'; d_in <= x"FF";
-
- wait;
- end process;
-
- process
- begin
- spi_din <= '1'; wait for 144.880 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 7.68 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.0 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 480 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 480 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 480 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 640 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 480 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 480 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 480 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;
Index: trunk/doc/SPI_state-diagram.jpg
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: trunk/doc/SPI_state-diagram.jpg
===================================================================
--- trunk/doc/SPI_state-diagram.jpg (revision 6)
+++ trunk/doc/SPI_state-diagram.jpg (nonexistent)
trunk/doc/SPI_state-diagram.jpg
Property changes :
Deleted: svn:mime-type
## -1 +0,0 ##
-application/octet-stream
\ No newline at end of property
Index: trunk/doc/spi-registers.txt
===================================================================
--- trunk/doc/spi-registers.txt (revision 6)
+++ trunk/doc/spi-registers.txt (nonexistent)
@@ -1,187 +0,0 @@
-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 low register (write)
- $F03B : address mid register (write)
- $F03C : address high register (write)
-
-
-These are the bits of the status register:
- busy: $01
- tx empty: $02
- rx ready: $04
- wait for data: $08
-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:
-; symbolic constant
-SECTOR .EQU $0F ; sector number
- .
- .
- .
- LDA SECTOR
- STA [SPI_AHI]
- LDA 0
- STA [SPI_AMID]
- 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 SECTOR
- STA [SPI_AHI]
- LDA BUF
- STA [SPI_AMID]
- 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. In the DIY Calculator we use only the topmost
-sector (no. 0x0F).
-
-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.
Index: spiflashcontroller/trunk/spi_ctrl.vhd
===================================================================
--- spiflashcontroller/trunk/spi_ctrl.vhd (nonexistent)
+++ spiflashcontroller/trunk/spi_ctrl.vhd (revision 7)
@@ -0,0 +1,630 @@
+--
+-- 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
+-- 0.2 15.Nov.2006 JH remove old code
+-- 1.0 5.Feb.2007 JH new clocking scheme
+-- 1.1 4.Apr.2007 JH implement high address byte
+-- 1.2 16.Apr.2007 JH clock enable
+-- 1.3 23.Apr.2007 JH remove all asynchronous elements
+-- 1.4 4.May 2007 JH resolve read timing
+-- 1.5 10.May 2007 JH remove read signal
+--
+
+
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.std_logic_unsigned.all;
+
+entity spi_ctrl is
+ port (
+ clk_in : 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;
+ wr : in std_logic;
+ addr : in std_logic_vector (2 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
+ -- clock generator
+ constant SYS_FREQ : integer := 25000000; -- 25MHz
+ constant SPI_FREQ : integer := 6250000; -- 6.25MHz
+ signal clk_en : std_logic;
+ signal clk_cnt : integer range 0 to (SYS_FREQ/SPI_FREQ)-1;
+
+ 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, rx_bit_cnt_clr : std_logic;
+ signal rx_bit_cnt : std_logic_vector (3 downto 0);
+
+ signal wr_cmd, wr_data, wr_add_h, wr_add_m, wr_add_l : std_logic;
+ signal rd_stat, rd_add_h, rd_add_m, rd_add_l : std_logic;
+ signal rd_data, rd_data1, rd_data2 : std_logic;
+ signal spi_cs_int, spi_clk_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 : std_logic_vector (7 downto 0);
+ signal add_h, 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 RDCMD: 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 <= spi_clk_int;
+ spi_dout <= tx_sreg(7);
+
+ -- clock generator
+ spi_divider : process (rst, clk_in)
+ begin
+ if rst = '1' then
+ clk_cnt <= 0;
+ clk_en <= '0';
+ spi_clk_int <= '1';
+ elsif falling_edge (clk_in) then
+ if clk_cnt = ((SYS_FREQ / SPI_FREQ) - 2) or
+ clk_cnt = ((SYS_FREQ / SPI_FREQ) - 3) then
+ clk_cnt <= clk_cnt + 1;
+ clk_en <= '0';
+ if tx_enable = '1' or rx_enable = '1' then
+ spi_clk_int <= '0';
+ else
+ spi_clk_int <= '1';
+ end if;
+ elsif clk_cnt = ((SYS_FREQ / SPI_FREQ) - 1) then
+ clk_cnt <= 0;
+ clk_en <= '1';
+ spi_clk_int <= '1';
+ else
+ clk_cnt <= clk_cnt + 1;
+ clk_en <= '0';
+ spi_clk_int <= '1';
+ end if;
+ end if;
+ end process;
+
+ -- address decoder
+ process (sel, addr, wr)
+ variable input : std_logic_vector (4 downto 0);
+ begin
+ input := sel & addr & wr;
+ -- defaults
+ wr_data <= '0';
+ wr_cmd <= '0';
+ wr_add_h <= '0';
+ wr_add_m <= '0';
+ wr_add_l <= '0';
+ rd_data <= '0';
+ rd_stat <= '0';
+ rd_add_h <= '0';
+ rd_add_m <= '0';
+ rd_add_l <= '0';
+ case input is
+ when "10000" => rd_data <= '1';
+ when "10001" => wr_data <= '1';
+ when "10010" => rd_stat <= '1';
+ when "10011" => wr_cmd <= '1';
+ when "10100" => rd_add_l <= '1';
+ when "10101" => wr_add_l <= '1';
+ when "10110" => rd_add_m <= '1';
+ when "10111" => wr_add_m <= '1';
+ when "11000" => rd_add_h <= '1';
+ when "11001" => wr_add_h <= '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_h(0) and rd_add_h)
+ 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_h(1) and rd_add_h)
+ 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_h(2) and rd_add_h)
+ 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_h(3) and rd_add_h)
+ 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_h(4) and rd_add_h)
+ 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_h(5) and rd_add_h)
+ 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_h(6) and rd_add_h)
+ 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_h(7) and rd_add_h)
+ or (add_m(7) and rd_add_m)
+ or (add_l(7) and rd_add_l);
+
+ -- write command register
+ process (rst, cmd_clr, clk_in)
+ begin
+ if rst = '1' or cmd_clr = '1' then
+ cmd <= NOP;
+ elsif rising_edge (clk_in) then
+ if wr_cmd = '1' then
+ cmd <= d_in;
+ end if;
+ end if;
+ end process;
+
+ -- write address high register
+ process (rst, clk_in)
+ begin
+ if rst = '1' then
+ add_h <= x"00";
+ elsif rising_edge (clk_in) then
+ if wr_add_h = '1' then
+ add_h <= d_in;
+ end if;
+ end if;
+ end process;
+
+ -- write address mid register
+ process (rst, clk_in)
+ begin
+ if rst = '1' then
+ add_m <= x"00";
+ elsif rising_edge (clk_in) then
+ if wr_add_m ='1' then
+ add_m <= d_in;
+ end if;
+ end if;
+ end process;
+
+ -- write address low register
+ process (rst, clk_in)
+ begin
+ if rst = '1' then
+ add_l <= x"00";
+ elsif rising_edge (clk_in) then
+ if wr_add_l ='1' then
+ add_l <= d_in;
+ end if;
+ end if;
+ end process;
+
+ -- write tx data register
+ process (rst, clk_in)
+ begin
+ if rst = '1' then
+ tx_data <= x"00";
+ elsif rising_edge (clk_in) then
+ if wr_data = '1' then
+ tx_data <= d_in;
+ end if;
+ 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, clk_in)
+ begin
+ if rst = '1' or tx_new_data_clr = '1' then
+ tx_new_data <= '0';
+ elsif rising_edge (clk_in) then
+ if wr_data ='1' then
+ tx_new_data <= '1';
+ end if;
+ end if;
+ end process;
+
+ -- advance the state machine
+ process (rst, clk_in)
+ begin
+ if rst = '1' then
+ state <= IDLE;
+ elsif rising_edge (clk_in) then
+ if clk_en = '1' then
+ state <= next_state;
+ end if;
+ 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
+ case cmd is
+ when WREN | WRDI | BE | DP => next_state <= CLR_CMD;
+ when SE | PP | RES | RDCMD | F_RD|WRSR|RDSR => next_state <= WAIT1;
+ when others => next_state <= CLR_CMD;
+ end case;
+ end if;
+
+ when WAIT1 =>
+ case cmd is
+ when WREN | WRDI | BE | DP => next_state <= CLR_CMD;
+ when SE | PP | RES | RDCMD | 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 | RDCMD | 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 | RDCMD => 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 RDCMD | 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 RDCMD | 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, cmd, tx_data, add_m, add_l, add_h)
+ begin
+ -- default values
+ tx_enable <= '0';
+ rx_enable <= '0';
+ rx_bit_cnt_clr <= '1';
+ 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 <= add_h;
+ 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_bit_cnt_clr <= '0';
+ 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_bit_cnt_clr <= '0';
+ spi_cs_int <= '1';
+ when CLR_CMD =>
+ cmd_clr <= '1';
+ when others => null;
+ end case;
+ end process;
+
+ -- the tx_empty flip flop
+ process (rst, wr_data, clk_in)
+ begin
+ if rst = '1' then
+ tx_empty <= '1';
+ elsif wr_data = '1' then
+ tx_empty <= '0';
+ elsif rising_edge (clk_in) then
+ if tx_empty_set = '1' then
+ tx_empty <= '1';
+ end if;
+ end if;
+ end process;
+
+ -- delay the tx_enable signal
+ process (rst, clk_in)
+ begin
+ if rst = '1' then
+ tx_enable_d <= '0';
+ elsif rising_edge (clk_in) then
+ tx_enable_d <= tx_enable;
+ end if;
+ end process;
+
+ -- transmitter shift register and bit counter
+ process (rst, tx_reg, tx_enable_d, clk_in)
+ begin
+ if rst = '1' then
+ tx_sreg <= x"FF";
+ tx_bit_cnt <= x"0";
+ tx_empty_set <= '0';
+ elsif tx_enable_d = '0' then
+ tx_sreg <= tx_reg;
+ tx_bit_cnt <= x"0";
+ tx_empty_set <= '0';
+ elsif rising_edge (clk_in) then
+ if clk_en = '1' then
+ tx_bit_cnt <= tx_bit_cnt + 1;
+ tx_sreg <= tx_sreg (6 downto 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 if;
+ end process;
+
+ -- synchronize rd_data
+ process (rst, clk_in)
+ begin
+ if rst = '1' then
+ rd_data1 <= '0';
+ elsif falling_edge (clk_in) then
+ rd_data1 <= rd_data;
+ end if;
+ end process;
+
+ process (rst, clk_in)
+ begin
+ if rst = '1' then
+ rd_data2 <= '0';
+ elsif falling_edge (clk_in) then
+ if rd_data = '0' then
+ rd_data2 <= rd_data1;
+ end if;
+ end if;
+ end process;
+
+ -- the rx_empty flip flop
+ process (rst, clk_in)
+ begin
+ if rst = '1' then
+ rx_empty <= '1';
+ elsif rising_edge (clk_in) 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_in)
+ begin
+ if rst = '1' then
+ rx_ready <= '0';
+ elsif rising_edge (clk_in) then
+ if rd_data = '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_in)
+ begin
+ if rst = '1' then
+ rx_data <= x"FF";
+ elsif rising_edge (clk_in) 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_bit_cnt_clr, clk_in)
+ begin
+ if rst = '1' or rx_bit_cnt_clr = '1' then
+ rx_bit_cnt <= x"0";
+ rx_ready_set <= '0';
+ rx_empty_clr <= '0';
+ rx_sreg <= x"FF";
+ elsif rising_edge (clk_in) then
+ if clk_en = '1' then
+ rx_sreg <= rx_sreg (6 downto 0) & spi_din;
+ case rx_bit_cnt is
+ when x"0" =>
+ rx_bit_cnt <= rx_bit_cnt + 1;
+ rx_ready_set <= '0';
+ rx_empty_clr <= '1';
+ when x"1" | x"2" | x"3" | x"4" | x"5" | x"6" =>
+ rx_bit_cnt <= rx_bit_cnt + 1;
+ rx_ready_set <= '0';
+ rx_empty_clr <= '0';
+ when x"7" =>
+ rx_bit_cnt <= rx_bit_cnt + 1;
+ rx_ready_set <= '1';
+ rx_empty_clr <= '0';
+ when others =>
+ null;
+ end case;
+ end if;
+ end if;
+ end process;
+end rtl;
Index: spiflashcontroller/trunk/tb_spi_ctrl.vhd
===================================================================
--- spiflashcontroller/trunk/tb_spi_ctrl.vhd (nonexistent)
+++ spiflashcontroller/trunk/tb_spi_ctrl.vhd (revision 7)
@@ -0,0 +1,410 @@
+
+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, rd, wr : std_logic;
+ signal addr : std_logic_vector (2 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 RDCMD: 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 (
+ clk_in => clk,
+ rst => rst,
+ spi_clk => spi_clk,
+ spi_cs => spi_cs,
+ spi_din => spi_din,
+ spi_dout => spi_dout,
+ sel => sel,
+ wr => wr,
+ addr => addr,
+ d_in => d_in,
+ d_out => d_out
+ );
+
+ process is
+ begin
+ clk <= '0'; wait for 20 ns;
+ clk <= '1'; wait for 20 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 <= "000"; rd <= '0'; wr <= '0'; d_in <= x"FF";
+ wait for 1420 ns;
+
+ -- write command WREN
+ sel <= '1'; addr <= "001"; d_in <= WREN; wait for 5 ns;
+ wr <= '1'; wait for 100 ns;
+ wr <= '0'; wait for 5 ns;
+ sel <= '0'; d_in <= x"FF"; wait for 2 us;
+
+ -- write command WRDI
+ sel <= '1'; addr <= "001"; d_in <= WRDI; wait for 5 ns;
+ wr <= '1'; wait for 100 ns;
+ wr <= '0'; wait for 5 ns;
+ sel <= '0'; d_in <= x"FF"; wait for 2 us;
+
+ -- write command WRSR: data
+ sel <= '1'; addr <= "000"; d_in <= x"55"; wait for 5 ns;
+ wr <= '1'; wait for 100 ns;
+ wr <= '0'; wait for 5 ns;
+ sel <= '0'; d_in <= x"FF"; wait for 10 ns;
+ -- the command
+ sel <= '1'; addr <= "001"; d_in <= WRSR; wait for 5 ns;
+ wr <= '1'; wait for 100 ns;
+ wr <= '0'; wait for 5 ns;
+ sel <= '0'; d_in <= x"FF"; wait for 4 us;
+
+ -- write command SE:
+ -- address low
+ sel <= '1'; addr <= "010"; d_in <= x"AB"; wait for 5 ns;
+ wr <= '1'; wait for 100 ns;
+ wr <= '0'; wait for 5 ns;
+ sel <= '0'; d_in <= x"FF"; wait for 10 ns;
+ --address mid
+ sel <= '1'; addr <= "011"; d_in <= x"CD"; wait for 5 ns;
+ wr <= '1'; wait for 100 ns;
+ wr <= '0'; wait for 5 ns;
+ sel <= '0'; d_in <= x"FF"; wait for 10 ns;
+ -- address high
+ sel <= '1'; addr <= "100"; d_in <= x"EF"; wait for 5 ns;
+ wr <= '1'; wait for 100 ns;
+ wr <= '0'; wait for 5 ns;
+ sel <= '0'; d_in <= x"FF"; wait for 10 ns;
+ -- the command
+ sel <= '1'; addr <= "001"; d_in <= SE; wait for 5 ns;
+ wr <= '1'; wait for 100 ns;
+ wr <= '0'; wait for 5 ns;
+ sel <= '0'; d_in <= x"FF"; wait for 6.5 us;
+
+ -- write command PP:
+ -- address low
+ sel <= '1'; addr <= "010"; d_in <= x"45"; wait for 5 ns;
+ wr <= '1'; wait for 100 ns;
+ wr <= '0'; wait for 5 ns;
+ sel <= '0'; d_in <= x"FF"; wait for 10 ns;
+ -- address mid
+ sel <= '1'; addr <= "011"; d_in <= x"67"; wait for 5 ns;
+ wr <= '1'; wait for 100 ns;
+ wr <= '0'; wait for 5 ns;
+ sel <= '0'; d_in <= x"FF"; wait for 10 ns;
+ -- address high
+ sel <= '1'; addr <= "100"; d_in <= x"89"; wait for 5 ns;
+ wr <= '1'; wait for 100 ns;
+ wr <= '0'; wait for 5 ns;
+ sel <= '0'; d_in <= x"FF"; wait for 10 ns;
+ -- the command
+ sel <= '1'; addr <= "001"; d_in <= PP; wait for 5 ns;
+ wr <= '1'; wait for 100 ns;
+ wr <= '0'; 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 <= "001"; wait for 5 ns;
+ rd <= '1'; wait for 100 ns;
+ stat <= d_out; rd <= '0'; wait for 5 ns;
+ sel <= '0'; wait for 1 us;
+ end loop;
+ -- write new data
+ sel <= '1'; addr <= "000";
+ d_in <= std_logic_vector(TO_UNSIGNED(i, d_in'Length)); wait for 5 ns;
+ wr <= '1'; wait for 100 ns;
+ wr <= '0'; 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 <= "000"; d_in <= x"AA"; wait for 5 ns;
+ wr <= '1'; wait for 100 ns;
+ wr <= '0'; wait for 5 ns;
+ sel <= '0'; d_in <= x"FF"; wait for 1 us;
+ -- write the NOP command to terminate
+ sel <= '1'; addr <= "001"; d_in <= NOP; wait for 5 ns;
+ wr <= '1'; wait for 100 ns;
+ wr <= '0'; 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 <= "001"; d_in <= RDSR; wait for 5 ns;
+ wr <= '1'; wait for 100 ns;
+ wr <= '0'; 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 <= "001"; wait for 5 ns;
+ rd <= '1'; wait for 100 ns;
+ stat <= d_out; rd <= '0'; wait for 5 ns;
+ sel <= '0';
+ end loop;
+ wait for 100 ns;
+ -- read the data
+ sel <= '1'; addr <= "000"; wait for 5 ns;
+ rd <= '1'; wait for 100 ns;
+ data <= d_out; rd <= '0'; wait for 5 ns;
+ sel <= '0'; wait for 1.5 us;
+
+ -- RES command
+ sel <= '1'; addr <= "001"; d_in <= RES; wait for 5 ns;
+ wr <= '1'; wait for 100 ns;
+ wr <= '0'; 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 <= "001"; wait for 5 ns;
+ rd <= '1'; wait for 100 ns;
+ stat <= d_out; rd <= '0'; wait for 5 ns;
+ sel <= '0';
+ end loop;
+ wait for 100 ns;
+ -- read the data
+ sel <= '1'; addr <= "000"; wait for 5 ns;
+ rd <= '1'; wait for 100 ns;
+ data <= d_out; rd <= '0'; wait for 5 ns;
+ sel <= '0'; wait for 1.5 us;
+
+ -- READ command
+ -- address low
+ sel <= '1'; addr <= "010"; d_in <= x"12"; wait for 5 ns;
+ wr <= '1'; wait for 100 ns;
+ wr <= '0'; wait for 5 ns;
+ sel <= '0'; d_in <= x"FF"; wait for 10 ns;
+ -- address mid
+ sel <= '1'; addr <= "011"; d_in <= x"34"; wait for 5 ns;
+ wr <= '1'; wait for 100 ns;
+ wr <= '0'; wait for 5 ns;
+ sel <= '0'; d_in <= x"FF"; wait for 10 ns;
+ -- address high
+ sel <= '1'; addr <= "100"; d_in <= x"56"; wait for 5 ns;
+ wr <= '1'; wait for 100 ns;
+ wr <= '0'; wait for 5 ns;
+ sel <= '0'; d_in <= x"FF"; wait for 10 ns;
+ -- the command
+ sel <= '1'; addr <= "001"; d_in <= RDCMD; wait for 5 ns;
+ wr <= '1'; wait for 100 ns;
+ wr <= '0'; 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 <= "001"; wait for 5 ns;
+ rd <= '1'; wait for 100 ns;
+ stat <= d_out; rd <= '0'; wait for 5 ns;
+ sel <= '0';
+ end loop;
+ wait for 100 ns;
+ -- read the data
+ sel <= '1'; addr <= "000"; wait for 5 ns;
+ rd <= '1'; wait for 100 ns;
+ data <= d_out; rd <= '0'; wait for 5 ns;
+ sel <= '0';
+ end loop;
+ wait for 1 us;
+ -- write the NOP command to terminate
+ sel <= '1'; addr <= "001"; d_in <= NOP; wait for 5 ns;
+ wr <= '1'; wait for 100 ns;
+ wr <= '0'; wait for 5 ns;
+ sel <= '0'; d_in <= x"FF";
+
+ wait;
+ end process;
+
+ process
+ begin
+ spi_din <= '1'; wait for 144.880 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 7.68 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.0 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 480 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 480 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 480 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 640 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 480 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 480 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 480 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;
Index: spiflashcontroller/trunk/doc/spi-registers.txt
===================================================================
--- spiflashcontroller/trunk/doc/spi-registers.txt (nonexistent)
+++ spiflashcontroller/trunk/doc/spi-registers.txt (revision 7)
@@ -0,0 +1,187 @@
+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 low register (write)
+ $F03B : address mid register (write)
+ $F03C : address high register (write)
+
+
+These are the bits of the status register:
+ busy: $01
+ tx empty: $02
+ rx ready: $04
+ wait for data: $08
+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:
+; symbolic constant
+SECTOR .EQU $0F ; sector number
+ .
+ .
+ .
+ LDA SECTOR
+ STA [SPI_AHI]
+ LDA 0
+ STA [SPI_AMID]
+ 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 SECTOR
+ STA [SPI_AHI]
+ LDA BUF
+ STA [SPI_AMID]
+ 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. In the DIY Calculator we use only the topmost
+sector (no. 0x0F).
+
+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.
Index: spiflashcontroller/trunk/doc/SPI_state-diagram.jpg
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: spiflashcontroller/trunk/doc/SPI_state-diagram.jpg
===================================================================
--- spiflashcontroller/trunk/doc/SPI_state-diagram.jpg (nonexistent)
+++ spiflashcontroller/trunk/doc/SPI_state-diagram.jpg (revision 7)
spiflashcontroller/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: spiflashcontroller/trunk
===================================================================
--- spiflashcontroller/trunk (nonexistent)
+++ spiflashcontroller/trunk (revision 7)
spiflashcontroller/trunk
Property changes :
Added: svn:mergeinfo
## -0,0 +0,0 ##
Index: spiflashcontroller/web_uploads
===================================================================
--- spiflashcontroller/web_uploads (nonexistent)
+++ spiflashcontroller/web_uploads (revision 7)
spiflashcontroller/web_uploads
Property changes :
Added: svn:mergeinfo
## -0,0 +0,0 ##
Index: spiflashcontroller/branches
===================================================================
--- spiflashcontroller/branches (nonexistent)
+++ spiflashcontroller/branches (revision 7)
spiflashcontroller/branches
Property changes :
Added: svn:mergeinfo
## -0,0 +0,0 ##
Index: spiflashcontroller/tags/V01/spi_ctrl.vhd
===================================================================
--- spiflashcontroller/tags/V01/spi_ctrl.vhd (nonexistent)
+++ spiflashcontroller/tags/V01/spi_ctrl.vhd (revision 7)
@@ -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;
Index: spiflashcontroller/tags/V01/tb_spi_ctrl.vhd
===================================================================
--- spiflashcontroller/tags/V01/tb_spi_ctrl.vhd (nonexistent)
+++ spiflashcontroller/tags/V01/tb_spi_ctrl.vhd (revision 7)
@@ -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;
+
+
+
Index: spiflashcontroller/tags/V01/doc/SPI_state-diagram.jpg
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: spiflashcontroller/tags/V01/doc/SPI_state-diagram.jpg
===================================================================
--- spiflashcontroller/tags/V01/doc/SPI_state-diagram.jpg (nonexistent)
+++ spiflashcontroller/tags/V01/doc/SPI_state-diagram.jpg (revision 7)
spiflashcontroller/tags/V01/doc/SPI_state-diagram.jpg
Property changes :
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: spiflashcontroller/tags/V01/doc/spi-registers.txt
===================================================================
--- spiflashcontroller/tags/V01/doc/spi-registers.txt (nonexistent)
+++ spiflashcontroller/tags/V01/doc/spi-registers.txt (revision 7)
@@ -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.
Index: spiflashcontroller/tags
===================================================================
--- spiflashcontroller/tags (nonexistent)
+++ spiflashcontroller/tags (revision 7)
spiflashcontroller/tags
Property changes :
Added: svn:mergeinfo
## -0,0 +0,0 ##