//////////////////////////////////////////////////////////////////////////////// // // Create Date: 21:53:19 09/29/06 // Module Name: mpu_spi_32 // Description: Implement a reduced wishbone implementation, moving to // full implementation eventually. This module use a 32 bit // wishbone bus. // // Revision: // Revision 0.01 - File Created // // File format: This file has been formated to use tabstop of 4 // // Available Command: // 0x01 - Address input, follow by a single write // 0x02 - Single byte write // 0x03 - Multi byte write, auto address increment // 0x04 - Single byte read // 0x05 - Multi byte read, auto address increment // Note: Write means to send data from SPI to Wishbone, read means // Wishbone to SPI // // Development platform: Spartan-3E Starter kit // // Copyright (C) 2006, Rick Huang // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library 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 // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // //////////////////////////////////////////////////////////////////////////////// module mpu_spi_32(spi_si, spi_so, spi_cs, spi_sck, /* SPI from mpu */ main_clk, /* Main system clock */ wADR_O, wSTB_O, wDAT_O, wDAT_I, wACK_I, wWE_O); /* SPI signals */ input spi_si; input spi_cs; input spi_sck; output spi_so; /* Wishbone bus signal, bus master */ input main_clk; output [31:0] wADR_O; output [31:0] wDAT_O; input [31:0] wDAT_I; output wSTB_O; output wWE_O; input wACK_I; /* Flipflops */ reg [7:0] shift_reg; reg [31:0] dat_i_reg; reg [3:0] spi_state; reg [4:0] cmd_state; reg [4:0] cmd_latch; reg [23:0] cmd_data_reg; reg [31:8] cmd_data_out_reg; reg [4:0] rcv_count; reg [31:0] wADR_O; reg [31:0] wDAT_O; reg [3:0] wbus_state; reg [4:0] wbus_timeout; reg wSTB_O; reg wWE_O; reg spi_si_int; reg spi_sck_int; reg spi_cs_int; reg addr_inc; /*************************************************** * Sync all external input to internal clock first * Input conditioning ***************************************************/ always @ (posedge main_clk) begin spi_si_int <= spi_si; spi_cs_int <= spi_cs; spi_sck_int <= spi_sck; end assign spi_so = shift_reg[7]; /* Last bit of shift reg */ /*************************************************** * Main tx/rx state machine ***************************************************/ parameter SPI_IDLE = 0, /* Declear bit states */ SPI_CLK_LOW = 1, SPI_CLK_HIGH = 2, SPI_COMPLETE = 3; parameter CMD_CMD = 0, /* Comand phase */ CMD_ADDR = 1, CMD_ADDR1 = 2, CMD_DATA_RX = 5, CMD_DATA_TX = 9, CMD_DATA_RX_MULTI = 13, CMD_DATA_TX_MULTI = 17, CMD_SHIFT0 = 19, CMD_SHIFT1 = 20, CMD_SHIFT2 = 21, CMD_SHIFT3 = 22, CMD_UNDEF = 24; parameter WBUS_IDLE = 0, /* Wishbone bus state */ WBUS_RD_START = 1, WBUS_RD_ACKED = 2, WBUS_WR_START = 3, WBUS_WR_ACKED = 4, WBUS_ERR = 5; parameter WBUS_TIMEOUT_SET = 5'h1f; always @ (posedge main_clk) begin if(spi_cs_int == 1) begin spi_state <= SPI_IDLE; /* Reset if CS is high anytime */ end else begin case (spi_state) SPI_IDLE: begin rcv_count <= 0; cmd_state <= CMD_CMD; if(spi_sck_int == 0) spi_state <= SPI_CLK_LOW; else spi_state <= SPI_CLK_HIGH; end SPI_CLK_LOW: if(spi_sck_int == 1) spi_state <= SPI_CLK_HIGH; SPI_CLK_HIGH: begin if(spi_sck_int == 0) /* High to low transition */ begin shift_reg[7:1] <= shift_reg[6:0]; shift_reg[0] <= spi_si_int; rcv_count <= rcv_count + 1; if(rcv_count == 7) /* Next cycle, rcv_count will be 8 */ spi_state <= SPI_COMPLETE; else spi_state <= SPI_CLK_LOW; end end SPI_COMPLETE: /* 1 byte completed */ begin rcv_count <= 0; /* Reset count */ spi_state <= SPI_CLK_LOW; case (cmd_state) CMD_CMD: begin if(shift_reg == 8'h01) /* shift_reg contains command */ begin cmd_latch <= CMD_ADDR; /* Address input mode */ cmd_state <= CMD_SHIFT0; end else if(shift_reg == 8'h02) begin cmd_latch <= CMD_DATA_RX; /* Single receive mode */ cmd_state <= CMD_SHIFT0; end else if(shift_reg == 8'h03) begin cmd_latch <= CMD_DATA_RX_MULTI; /* Multi RX */ cmd_state <= CMD_SHIFT0; end else if(shift_reg == 8'h04) begin wbus_state <= WBUS_RD_START; cmd_state <= CMD_DATA_TX; /* Single transmit mode */ end else if(shift_reg == 8'h05) begin wbus_state <= WBUS_RD_START; cmd_state <= CMD_DATA_TX_MULTI; /* Multi TX */ end end // When shifting data CMD_SHIFT0: begin cmd_data_reg[7:0] <= shift_reg; shift_reg <= cmd_data_out_reg[15:8]; cmd_state <= CMD_SHIFT1; end CMD_SHIFT1: begin cmd_data_reg[15:8] <= shift_reg; shift_reg <= cmd_data_out_reg[23:16]; cmd_state <= CMD_SHIFT2; end CMD_SHIFT2: begin cmd_data_reg[23:16] <= shift_reg; shift_reg <= cmd_data_out_reg[31:24]; // Decide where to go next // cmd_state <= cmd_latch; // Define the states individually, // so synthesizer know to convert it to one hot encoding case (cmd_latch) CMD_DATA_RX: cmd_state <= CMD_DATA_RX; CMD_UNDEF: cmd_state <= CMD_UNDEF; CMD_SHIFT0: cmd_state <= CMD_SHIFT0; CMD_DATA_RX_MULTI: cmd_state <= CMD_DATA_RX_MULTI; CMD_DATA_TX_MULTI: cmd_state <= CMD_DATA_TX_MULTI; CMD_ADDR: cmd_state <= CMD_ADDR; endcase end // Real command finished here CMD_ADDR: begin wADR_O[31:0] <= {shift_reg, cmd_data_reg[23:0]}; cmd_latch <= CMD_DATA_RX; cmd_state <= CMD_SHIFT0; end CMD_DATA_RX: begin wDAT_O[31:0] <= {shift_reg, cmd_data_reg[23:0]}; cmd_state <= CMD_UNDEF; wbus_state <= WBUS_WR_START; end CMD_DATA_RX_MULTI: begin wDAT_O[31:0] <= {shift_reg, cmd_data_reg[23:0]}; cmd_latch <= CMD_DATA_RX_MULTI; cmd_state <= CMD_SHIFT0; wbus_state <= WBUS_WR_START; end CMD_DATA_TX: begin cmd_state <= CMD_SHIFT0; cmd_latch <= CMD_UNDEF; shift_reg <= dat_i_reg[7:0]; cmd_data_out_reg[31:8] <= dat_i_reg[31:8]; end CMD_DATA_TX_MULTI: begin wADR_O <= wADR_O + 4; wbus_state <= WBUS_RD_START; cmd_state <= CMD_SHIFT0; cmd_latch <= CMD_DATA_TX_MULTI; shift_reg <= dat_i_reg[7:0]; cmd_data_out_reg[31:8] <= dat_i_reg[31:8]; end CMD_UNDEF: cmd_state <= CMD_UNDEF; /* No more data accepted */ default: cmd_state <= CMD_CMD; endcase end default: begin spi_state <= SPI_IDLE; end endcase case (wbus_state) /* WBus runs */ WBUS_RD_START: if(wACK_I == 0) begin wSTB_O <= 1; /* Start a read cycle */ wWE_O <= 0; wbus_timeout <= wbus_timeout - 1; if(wbus_timeout == 0) wbus_state <= WBUS_ERR; end else begin wbus_state <= WBUS_RD_ACKED; /* Received a ACK signal */ wSTB_O <= 0; dat_i_reg <= wDAT_I; end WBUS_RD_ACKED: begin if(wACK_I == 0) begin wbus_state <= WBUS_IDLE; end end WBUS_WR_START: if(wACK_I == 0) begin wSTB_O <= 1; /* Start a write cycle */ wWE_O <= 1; wbus_timeout <= wbus_timeout - 1; if(wbus_timeout == 0) wbus_state <= WBUS_ERR; end else begin wbus_state <= WBUS_WR_ACKED; /* Received a ACK signal */ wSTB_O <= 0; wWE_O <= 0; end WBUS_WR_ACKED: begin if(wACK_I == 0) begin wbus_state <= WBUS_IDLE; if(cmd_latch == CMD_DATA_RX_MULTI) addr_inc <= 1; else addr_inc <= 0; end end WBUS_IDLE: begin wSTB_O <= 0; wbus_timeout <= WBUS_TIMEOUT_SET; if(addr_inc) begin wADR_O <= wADR_O + 4; addr_inc <= 0; end end WBUS_ERR: begin wSTB_O <= 0; wbus_state <= WBUS_IDLE; end endcase end end endmodule