blob: 450276debad91fe09d267c4dee1c874dc184a30a [file] [log] [blame]
//////////////////////////////////////////////////////////////////////
//// ////
//// tiny_spi.v ////
//// ////
//// This file is part of the TINY SPI IP core project ////
//// http://www.opencores.org/projects/tiny_spi/ ////
//// ////
//// Author(s): ////
//// - Thomas Chou <thomas@wytron.com.tw> ////
//// ////
//// All additional information is avaliable in the README ////
//// file. ////
//// ////
//////////////////////////////////////////////////////////////////////
//// ////
//// Copyright (C) 2010 Authors ////
//// ////
//// 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 source file 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 source 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 source; if not, download it ////
//// from http://www.opencores.org/lgpl.shtml ////
//// ////
//////////////////////////////////////////////////////////////////////
module tiny_spi (
`ifdef USE_POWER_PINS
input wire vccd1, // User area 1 1.8V supply
input wire vssd1, // User area 1 digital ground
`endif
// system
input wire rst_i,
input wire clk_i,
// memory mapped
input wire stb_i,
input wire we_i ,
output wire [31:0] dat_o,
input wire [31:0] dat_i,
output wire int_o,
input wire [ 2:0] adr_i,
input wire cyc_i, // comment out for avalon
output wire ack_o, // comment out for avalon
// spi
output wire MOSI ,
output wire SCLK ,
input wire MISO
);
parameter BAUD_WIDTH = 8;
parameter BAUD_DIV = 8;
parameter SPI_MODE = 0;
parameter BC_WIDTH = 3;
//parameter DIV_WIDTH = BAUD_DIV ? $clog2(BAUD_DIV / 2 - 1) : BAUD_WIDTH;
parameter DIV_WIDTH = $clog2(BAUD_DIV / 2 - 1);
reg [ 7:0] sr8, bb8;
wire [ 7:0] sr8_sf;
reg [ BC_WIDTH-1:0] bc, bc_next;
reg [DIV_WIDTH-1:0] ccr ;
reg [DIV_WIDTH-1:0] cc, cc_next;
wire misod ;
wire cstb, wstb, bstb, istb;
reg sck ;
reg sf, ld;
reg bba ; // buffer flag
reg txren, txeen;
wire txr, txe;
wire cpol, cpha;
reg cpolr, cphar;
wire wr ;
// wire cyc_i; // comment out for wishbone
// wire ack_o; // comment out for wishbone
// assign cyc_i = 1'b1; // comment out for wishbone
assign ack_o = stb_i & cyc_i; // zero wait
assign wr = stb_i & cyc_i & we_i & ack_o;
assign wstb = wr & (adr_i == 1);
assign istb = wr & (adr_i == 2);
assign cstb = wr & (adr_i == 3);
assign bstb = wr & (adr_i == 4);
assign sr8_sf = { sr8[6:0],misod };
assign dat_o =
(sr8 & {8{(adr_i == 0)}})
| (bb8 & {8{(adr_i == 1)}})
| ({ txr, txe } & {8{(adr_i == 2)}})
;
parameter
IDLE = 0,
PHASE1 = 1,
PHASE2 = 2
;
reg [1:0] spi_seq, spi_seq_next;
always @(posedge clk_i )
if (rst_i)
spi_seq <= IDLE;
else
spi_seq <= spi_seq_next;
wire high_val, low_val;
assign high_val = 1'b1;
assign low_val = 1'b0;
always @(posedge clk_i)
begin
if(rst_i)
begin
cc <= {2{1'b0}};
bc <= {3{1'b0}};
end
else
begin
cc <= cc_next;
bc <= bc_next;
end
end
always @(/*AS*/bba or bc or cc or ccr or cpha or cpol or spi_seq)
begin
sck = cpol;
cc_next = BAUD_DIV ? (BAUD_DIV / 2 - 1) : ccr;
bc_next = bc;
ld = 1'b0;
sf = 1'b0;
case (spi_seq)
IDLE :
begin
if (bba)
begin
bc_next = 7;
ld = 1'b1;
spi_seq_next = PHASE2;
end
else
spi_seq_next = IDLE;
end
PHASE2 :
begin
sck = (cpol ^ cpha);
if (cc == 0)
spi_seq_next = PHASE1;
else
begin
cc_next = cc - 1;
spi_seq_next = PHASE2;
end
end
PHASE1 :
begin
sck = ~(cpol ^ cpha);
if (cc == 0)
begin
bc_next = bc -1;
sf = 1'b1;
if (bc == 0)
begin
if (bba)
begin
bc_next = 7;
ld = 1'b1;
spi_seq_next = PHASE2;
end
else
spi_seq_next = IDLE;
end
else
spi_seq_next = PHASE2;
end
else
begin
cc_next = cc - 1;
spi_seq_next = PHASE1;
end
end
endcase
end // always @ (...
always @(posedge clk_i)
begin
if(rst_i)
begin
{ cpolr, cphar } <= 2'b0;
{ txren, txeen } <= 2'b0;
ccr <= 8'd0;
sr8 <= 8'd0;
bb8 <= 8'd0;
end
else
begin
if (cstb) // control reg
{ cpolr, cphar } <= dat_i;
else
{ cpolr, cphar } <= { cpolr, cphar };
if (istb) // irq enable reg
{ txren, txeen } <= dat_i;
else
{ txren, txeen } <= { txren, txeen };
if (bstb) // baud reg
ccr <= dat_i;
else
ccr <= ccr;
if (ld) // shift reg
sr8 <= bb8;
else if (sf)
sr8 <= sr8_sf;
else
sr8 <= sr8;
if (wstb) // buffer reg
bb8 <= dat_i;
else if (ld)
bb8 <= (spi_seq == IDLE) ? sr8 : sr8_sf;
else
bb8 <= bb8;
end
end // always @ (posedge clk_i)
always @(posedge clk_i)
begin
if (rst_i)
bba <= 1'b0;
else if (wstb)
bba <= 1'b1;
else if (ld)
bba <= 1'b0;
else
bba <= bba;
end
assign { cpol, cpha } = ((SPI_MODE >= 0) & (SPI_MODE < 4)) ?
SPI_MODE : { cpolr, cphar };
assign txe = (spi_seq == IDLE);
assign txr = ~bba;
assign int_o = (txr & txren) | (txe & txeen);
assign SCLK = sck;
assign MOSI = sr8[7];
assign misod = MISO;
endmodule