336 lines
10 KiB
Verilog
336 lines
10 KiB
Verilog
/////////////////////////////////////////////////////////////////////
|
|
//// ////
|
|
//// 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
|
|
|