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

Subversion Repositories spiflashcontroller

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /
    from Rev 6 to Rev 7
    Reverse comparison

Rev 6 → Rev 7

/tags/V01/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/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: 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: 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 ##

powered by: WebSVN 2.1.0

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