///////////////////////////////////////////////////////////////////// //// //// //// OpenCores MC68HC11E based SPI interface //// //// //// //// Author: Richard Herveille //// //// richard@asics.ws //// //// www.asics.ws //// //// //// ///////////////////////////////////////////////////////////////////// //// //// //// Copyright (C) 2002 Richard Herveille //// //// richard@asics.ws //// //// //// //// This source file may be used and distributed without //// //// restriction provided that this copyright statement is not //// //// removed from the file and that any derivative work contains //// //// the original copyright notice and the associated disclaimer.//// //// //// //// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// //// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// //// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// //// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// //// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// //// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// //// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// //// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// //// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// //// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// //// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// //// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// //// POSSIBILITY OF SUCH DAMAGE. //// //// //// ///////////////////////////////////////////////////////////////////// // CVS Log // // $Id: simple_spi_top.v,v 1.5 2004-02-28 15:59:50 rherveille Exp $ // // $Date: 2004-02-28 15:59:50 $ // $Revision: 1.5 $ // $Author: rherveille $ // $Locker: $ // $State: Exp $ // // Change History: // $Log: not supported by cvs2svn $ // Revision 1.4 2003/08/01 11:41:54 rherveille // Fixed some timing bugs. // // Revision 1.3 2003/01/09 16:47:59 rherveille // Updated clkcnt size and decoding due to new SPR bit assignments. // // Revision 1.2 2003/01/07 13:29:52 rherveille // Changed SPR bits coding. // // Revision 1.1.1.1 2002/12/22 16:07:15 rherveille // Initial release // // // // Motorola MC68HC11E based SPI interface // // Currently only MASTER mode is supported // module simple_spi #( parameter SS_WIDTH = 1 )( // 8bit WISHBONE bus slave interface input wire clk_i, // clock input wire rst_i, // reset (synchronous active high) input wire cyc_i, // cycle input wire stb_i, // strobe input wire [2:0] adr_i, // address input wire we_i, // write enable input wire [7:0] dat_i, // data input output reg [7:0] dat_o, // data output output reg ack_o, // normal bus termination output reg inta_o, // interrupt output // SPI port output reg sck_o, // serial clock output output [SS_WIDTH-1:0] ss_o, // slave select (active low) output wire mosi_o, // MasterOut SlaveIN input wire miso_i // MasterIn SlaveOut ); // // Module body // reg [7:0] spcr; // Serial Peripheral Control Register ('HC11 naming) wire [7:0] spsr; // Serial Peripheral Status Register ('HC11 naming) reg [7:0] sper; // Serial Peripheral Extension Register reg [7:0] treg; // Transmit Register reg [SS_WIDTH-1:0] ss_r; // Slave Select Register // fifo signals wire [7:0] rfdout; reg wfre, rfwe; wire rfre, rffull, rfempty; wire [7:0] wfdout; wire wfwe, wffull, wfempty; // misc signals wire tirq; // transfer interrupt (selected number of transfers done) wire wfov; // write fifo overrun (writing while fifo full) reg [1:0] state; // statemachine state reg [2:0] bcnt; // // Wishbone interface wire wb_acc = cyc_i & stb_i; // WISHBONE access wire wb_wr = wb_acc & we_i; // WISHBONE write access // dat_i always @(posedge clk_i) if (rst_i) begin spcr <= 8'h10; // set master bit sper <= 8'h00; ss_r <= 0; end else if (wb_wr) begin if (adr_i == 3'b000) spcr <= dat_i | 8'h10; // always set master bit if (adr_i == 3'b011) sper <= dat_i; if (adr_i == 3'b100) ss_r <= dat_i[SS_WIDTH-1:0]; end // slave select (active low) assign ss_o = ~ss_r; // write fifo assign wfwe = wb_acc & (adr_i == 3'b010) & ack_o & we_i; assign wfov = wfwe & wffull; // dat_o always @(posedge clk_i) case(adr_i) // synopsys full_case parallel_case 3'b000: dat_o <= spcr; 3'b001: dat_o <= spsr; 3'b010: dat_o <= rfdout; 3'b011: dat_o <= sper; 3'b100: dat_o <= {{ (8-SS_WIDTH){1'b0} }, ss_r}; default: dat_o <= 0; endcase // read fifo assign rfre = wb_acc & (adr_i == 3'b010) & ack_o & ~we_i; // ack_o always @(posedge clk_i) if (rst_i) ack_o <= 1'b0; else ack_o <= wb_acc & !ack_o; // decode Serial Peripheral Control Register wire spie = spcr[7]; // Interrupt enable bit wire spe = spcr[6]; // System Enable bit wire dwom = spcr[5]; // Port D Wired-OR Mode Bit wire mstr = spcr[4]; // Master Mode Select Bit wire cpol = spcr[3]; // Clock Polarity Bit wire cpha = spcr[2]; // Clock Phase Bit wire [1:0] spr = spcr[1:0]; // Clock Rate Select Bits // decode Serial Peripheral Extension Register wire [1:0] icnt = sper[7:6]; // interrupt on transfer count wire [1:0] spre = sper[1:0]; // extended clock rate select wire [3:0] espr = {spre, spr}; // generate status register wire wr_spsr = wb_wr & (adr_i == 3'b001); reg spif; always @(posedge clk_i) if (~spe | rst_i) spif <= 1'b0; else spif <= (tirq | spif) & ~(wr_spsr & dat_i[7]); reg wcol; always @(posedge clk_i) if (~spe | rst_i) wcol <= 1'b0; else wcol <= (wfov | wcol) & ~(wr_spsr & dat_i[6]); assign spsr[7] = spif; assign spsr[6] = wcol; assign spsr[5:4] = 2'b00; assign spsr[3] = wffull; assign spsr[2] = wfempty; assign spsr[1] = rffull; assign spsr[0] = rfempty; // generate IRQ output (inta_o) always @(posedge clk_i) inta_o <= spif & spie; // // hookup read/write buffer fifo fifo4 #(8) rfifo( .clk ( clk_i ), .rst ( ~rst_i ), .clr ( ~spe ), .din ( treg ), .we ( rfwe ), .dout ( rfdout ), .re ( rfre ), .full ( rffull ), .empty ( rfempty ) ), wfifo( .clk ( clk_i ), .rst ( ~rst_i ), .clr ( ~spe ), .din ( dat_i ), .we ( wfwe ), .dout ( wfdout ), .re ( wfre ), .full ( wffull ), .empty ( wfempty ) ); // // generate clk divider reg [11:0] clkcnt; always @(posedge clk_i) if(spe & (|clkcnt & |state)) clkcnt <= clkcnt - 11'h1; else case (espr) // synopsys full_case parallel_case 4'b0000: clkcnt <= 12'h0; // 2 -- original M68HC11 coding 4'b0001: clkcnt <= 12'h1; // 4 -- original M68HC11 coding 4'b0010: clkcnt <= 12'h3; // 16 -- original M68HC11 coding 4'b0011: clkcnt <= 12'hf; // 32 -- original M68HC11 coding 4'b0100: clkcnt <= 12'h1f; // 8 4'b0101: clkcnt <= 12'h7; // 64 4'b0110: clkcnt <= 12'h3f; // 128 4'b0111: clkcnt <= 12'h7f; // 256 4'b1000: clkcnt <= 12'hff; // 512 4'b1001: clkcnt <= 12'h1ff; // 1024 4'b1010: clkcnt <= 12'h3ff; // 2048 4'b1011: clkcnt <= 12'h7ff; // 4096 default : clkcnt <= 12'hfff; endcase // generate clock enable signal wire ena = ~|clkcnt; // transfer statemachine always @(posedge clk_i) if (~spe | rst_i) begin state <= 2'b00; // idle bcnt <= 3'h0; treg <= 8'h00; wfre <= 1'b0; rfwe <= 1'b0; sck_o <= 1'b0; end else begin wfre <= 1'b0; rfwe <= 1'b0; case (state) //synopsys full_case parallel_case 2'b00: // idle state begin bcnt <= 3'h7; // set transfer counter treg <= wfdout; // load transfer register sck_o <= cpol; // set sck if (~wfempty) begin wfre <= 1'b1; state <= 2'b01; if (cpha) sck_o <= ~sck_o; end end 2'b01: // clock-phase2, next data if (ena) begin sck_o <= ~sck_o; state <= 2'b11; end 2'b11: // clock phase1 if (ena) begin treg <= {treg[6:0], miso_i}; bcnt <= bcnt -3'h1; if (~|bcnt) begin state <= 2'b00; sck_o <= cpol; rfwe <= 1'b1; end else begin state <= 2'b01; sck_o <= ~sck_o; end end 2'b10: state <= 2'b00; endcase end assign mosi_o = treg[7]; // count number of transfers (for interrupt generation) reg [1:0] tcnt; // transfer count always @(posedge clk_i) if (~spe) tcnt <= icnt; else if (rfwe) // rfwe gets asserted when all bits have been transfered if (|tcnt) tcnt <= tcnt - 2'h1; else tcnt <= icnt; assign tirq = ~|tcnt & rfwe; endmodule