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

Subversion Repositories sdspi

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /sdspi
    from Rev 2 to Rev 3
    Reverse comparison

Rev 2 → Rev 3

/trunk/README.md
0,0 → 1,45
# SD-Card controller, using a shared SPI interface
 
This Verilog core exports an SD card controller interface from internal to an
FPGA to the rest of the FPGA core, while taking care of the lower level details
internal to the interface. Unlike the [other OpenCores SD Card controller](http://www.opencores.org/project,sdcard_mass_storage_controller) which offers a full SD interface, this controller focuses on the SPI interface of the SD Card.
While this is a slower interface, the SPI interface is
necessary to access the card when using a [XuLA2 board](http://www.xess.com/shop/product/xula2-lx25/), or
in general any time the full 9--bit, bi--directional interface to the SD card
has not been implemented.
Further, for those who are die--hard Verilog authors, this core is written in
Verilog as opposed to the [XESS provided demonstration SD Card controller
found on GitHub](https://github.com/xesscorp/VHDL\_Lib/SDCard.vhd), which was
written
in VHDL. For those who are not such die--hard Verilog authors, this controller
provides a lower level interface to the card than these other controllers.
Whereas the XESS controller will automatically start up the card and interact
with it, this controller requires external software to be used when interacting
with the card. This makes this SDSPI controller both more versatile, in the
face of potential changes to the card interface, but also less turn-key.
 
While this core was written for the purpose of being used with the [ZipCPU](https://github.com/ZipCPU/zipcpu),
as enhanced by the Wishbone DMA controller used by the ZipCPU, nothing in this
core prevents it from being used with any other architecture that supports
the 32-bit Wishbone interface of this core.
 
This core has been written as a wishbone slave, not a master. Using the core
together with a separate master, such as a CPU or a DMA controller, only makes
sense. This design choice, however, also restricts the core from being able to
use the multiple block write or multiple block read commands, restricting us to
single block read and write commands alone.
 
For more information, please consult the specification document.
 
# Next Steps
 
Now that I have an initial SD Card controller working over the SPI port, I've
kind of fallen in love with the simple interface it uses. I'm wondering if I
can use the same control interface for the full SD protocol. To that end, I
intend to build a version of this controller that works with the full SD
protocol--even integrating a card detect bit into the control register.
 
# Commercial Applications
 
Should you find the GPLv3 license insufficient for your needs, other licenses
can be purchased from Gisselquist Technology, LLC.
/trunk/bench/cpp/sdspisim.cpp
1,6 → 1,5
///////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
//
// Filename: sdspisim.cpp
//
// Project: Wishbone Controlled SD-Card Controller over SPI port
11,12 → 10,12
// This simulator is for testing use in a Verilator/C++ environment, where
// it would be used in place of the actual hardware.
//
// Creator: Dan Gisselquist
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
///////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2016, Gisselquist Technology, LLC
// Copyright (C) 2015-2017, Gisselquist Technology, LLC
//
// This program is free software (firmware): you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
29,7 → 28,7
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program. (It's in the $(ROOT)/doc directory, run make with no
// with this program. (It's in the $(ROOT)/doc directory. Run make with no
// target there if the PDF file isn't present.) If not, see
// <http://www.gnu.org/licenses/> for a copy.
//
37,7 → 36,9
// http://www.gnu.org/licenses/gpl.html
//
//
///////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
//
#include <stdio.h>
#include <string.h>
#include <assert.h>
69,7 → 70,7
static const unsigned
CCS = 1; // 0: SDSC card, 1: SDHC or SDXC card
 
SDSPISIM::SDSPISIM(void) {
SDSPISIM::SDSPISIM(const bool debug) {
m_dev = NULL;
m_last_sck = 1;
m_block_address = (CCS==1);
120,6 → 121,7
//
m_reading_data = false;
m_have_token = false;
m_debug = debug;
}
 
void SDSPISIM::load(const char *fname) {
133,7 → 135,7
 
m_devblocks = devln>>9;
 
printf("SDCARD: NBLOCKS = %ld\n", m_devblocks);
if (m_debug) printf("SDCARD: NBLOCKS = %ld\n", m_devblocks);
}
}
 
181,7 → 183,7
// Only change our output on the falling edge
 
m_last_sck = sck;
printf("SDSPI: (%3d) [%d,%d,%d] ", m_delay, csn, sck, m_mosi);
if (m_debug) printf("SDSPI: (%3d) [%d,%d,%d] ", m_delay, csn, sck, m_mosi);
// assert(m_delay > 20);
 
m_bitpos++;
188,15 → 190,15
m_dat_in = (m_dat_in<<1)|m_mosi;
 
 
printf("(bitpos=%d,dat_in=%02x)\n", m_bitpos&7, m_dat_in&0x0ff);
if (m_debug) printf("(bitpos=%d,dat_in=%02x)\n", m_bitpos&7, m_dat_in&0x0ff);
 
if ((m_bitpos&7)==0) {
printf("SDSPI--RX BYTE %02x\n", m_dat_in&0x0ff);
if (m_debug) printf("SDSPI--RX BYTE %02x\n", m_dat_in&0x0ff);
m_dat_out = 0xff;
if (m_reading_data) {
if (m_have_token) {
m_block_buf[m_rxloc++] = m_dat_in;
printf("SDSPI: WR[%3d] = %02x\n", m_rxloc-1,
if (m_debug) printf("SDSPI: WR[%3d] = %02x\n", m_rxloc-1,
m_dat_in&0x0ff);
if (m_rxloc >= 512+2) {
unsigned crc, rxcrc;
204,8 → 206,8
rxcrc = ((m_block_buf[512]&0x0ff)<<8)
|(m_block_buf[513]&0x0ff);
 
printf("LEN = %d\n", m_rxloc);
printf("CHECKING CRC: (rx) %04x =? %04x (calc)\n",
if (m_debug) printf("LEN = %d\n", m_rxloc);
if (m_debug) printf("CHECKING CRC: (rx) %04x =? %04x (calc)\n",
crc, rxcrc);
m_reading_data = false;
m_have_token = false;
218,13 → 220,14
}
} else {
if ((m_dat_in&0x0ff) == 0x0fe) {
printf("SDSPI: TOKEN!!\n");
if (m_debug) printf("SDSPI: TOKEN!!\n");
m_have_token = true;
m_rxloc = 0;
} else printf("SDSPI: waiting on token\n");
} else if (m_debug)
printf("SDSPI: waiting on token\n");
}
} else if (m_cmdidx < 6) {
printf("SDSPI: CMDIDX = %d\n", m_cmdidx);
if (m_debug) printf("SDSPI: CMDIDX = %d\n", m_cmdidx);
// All commands *must* start with a 01... pair of bits.
if (m_cmdidx == 0)
assert((m_dat_in&0xc0)==0x40);
236,10 → 239,12
m_rspidx = 0;
m_blkdly = 0;
m_blkidx = SDSPI_MAXBLKLEN;
printf("SDSPI: CMDIDX = %d -- WE HAVE A COMMAND! [ ", m_cmdidx);
for(int i=0; i<6; i++)
printf("%02x ", m_cmdbuf[i] & 0xff);
printf("]\n"); fflush(stdout);
if (m_debug) {
printf("SDSPI: CMDIDX = %d -- WE HAVE A COMMAND! [ ", m_cmdidx);
for(int i=0; i<6; i++)
printf("%02x ", m_cmdbuf[i] & 0xff);
printf("]\n"); fflush(stdout);
}
 
unsigned arg;
arg = ((((((m_cmdbuf[1]<<8)|(m_cmdbuf[2]&0x0ff))<<8)
292,7 → 297,7
} else {
m_altcmd_flag = false;
memset(m_rspbuf, 0x0ff, SDSPI_RSPLEN);
printf("SDSPI: Received a command 0x%02x (%d)\n",
if (m_debug) printf("SDSPI: Received a command 0x%02x (%d)\n",
m_cmdbuf[0], m_cmdbuf[0]&0x03f);
switch(m_cmdbuf[0]&0x3f) {
case 0: // CMD0 -- GO_IDLE_STATE
367,7 → 372,7
m_rspbuf[0] = 0x00;
memset(m_block_buf, 0x0ff, SDSPI_MAXBLKLEN);
if (m_dev) {
printf("Reading from block %08x of %08lx\n", arg, m_devblocks);
if (m_debug) printf("Reading from block %08x of %08lx\n", arg, m_devblocks);
if (m_block_address) {
assert(arg < m_devblocks);
fseek(m_dev, arg<<9, SEEK_SET);
378,9 → 383,10
} m_block_buf[0] = 0x0fe;
m_blklen = 512; // (1<<m_csd[5]);
if (m_dev)
fread(&m_block_buf[1], m_blklen, 1, m_dev);
m_blklen = fread(&m_block_buf[1], m_blklen, 1, m_dev);
else
memset(&m_block_buf[1], 0, m_blklen);
m_blklen = (m_blklen != 512) ? 512 : m_blklen;
add_block_crc(m_blklen, m_block_buf);
 
m_blkdly = 60;
423,7 → 429,7
default: // Unimplemented command
m_rspbuf[0] = 0x04;
m_rspdly = 4;
printf("SDSPI ERR: Command CMD%d not implemented!\n", m_cmdbuf[0]&0x03f);
if (m_debug) printf("SDSPI ERR: Command CMD%d not implemented!\n", m_cmdbuf[0]&0x03f);
fflush(stdout);
assert(0 && "Not Implemented");
}
478,7 → 484,7
 
bool SDSPISIM::check_cmdcrc(char *buf) const {
unsigned fill = cmdcrc(5, buf);
printf("SDSPI: CRC-CHECK, should have a CRC of %02x\n", fill);
if (m_debug) printf("SDSPI: CRC-CHECK, should have a CRC of %02x\n", fill);
return (fill == (buf[5]&0x0ff));
}
 
/trunk/bench/cpp/sdspisim.h
15,7 → 15,7
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2016, Gisselquist Technology, LLC
// Copyright (C) 2015-2017, Gisselquist Technology, LLC
//
// This program is free software (firmware): you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
28,7 → 28,7
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program. (It's in the $(ROOT)/doc directory, run make with no
// with this program. (It's in the $(ROOT)/doc directory. Run make with no
// target there if the PDF file isn't present.) If not, see
// <http://www.gnu.org/licenses/> for a copy.
//
75,7 → 75,7
char m_csd[SDSPI_CSDLEN], m_cid[SDSPI_CIDLEN];
 
public:
SDSPISIM(void);
SDSPISIM(const bool debug = false);
void load(const char *fname);
void debug(const bool dbg) { m_debug = dbg; }
bool debug(void) const { return m_debug; }
/trunk/doc/Makefile
1,3 → 1,48
################################################################################
##
## Filename: Makefile
##
## Project: SD-Card controller, using a shared SPI interface
##
## Purpose: To coordinate the build of documentation PDFs from their
## LaTeX sources.
##
## Targets include:
## all Builds all documents
## gpl-3.0.pdf Builds the GPL license these files are released
## under.
## spec.pdf Builds the specification for the SDSPI
## controller.
##
## Creator: Dan Gisselquist, Ph.D.
## Gisselquist Technology, LLC
##
################################################################################
##
## Copyright (C) 2016, Gisselquist Technology, LLC
##
## This program is free software (firmware): 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 3 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 MERCHANTIBILITY 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. (It's in the $(ROOT)/doc directory, run make with no
## target there if the PDF file isn't present.) If not, see
## <http://www.gnu.org/licenses/> for a copy.
##
## License: GPL, v3, as defined and found on www.gnu.org,
## http://www.gnu.org/licenses/gpl.html
##
##
################################################################################
##
##
all: gpl-3.0.pdf spec.pdf
DSRC := src
 
/trunk/doc/spec.pdf Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream
/trunk/doc/src/spec.tex
53,7 → 53,7
\project{SDSPI Controller}
\title{Specification}
\author{Dan Gisselquist, Ph.D.}
\email{dgisselq (at) opencores.org}
\email{dgisselq (at) ieee.org}
\revision{Rev.~0.1}
\begin{document}
\pagestyle{gqtekspecplain}
335,7 → 335,7
shouldn't be busy anyway), and we then clear any errors:
\begin{tabbing}
{\tt SD\_WAIT\_WHILE\_BUSY;} \\
{\tt CMD} \= {\tt SD\_CLEARERR};
{\tt CMD} \= {\tt = SD\_CLEARERR};
\end{tabbing}
 
Now that the controller is idle (which it should've been from startup anyway),
458,7 → 458,7
\begin{tabbing}
{\tt int CSD[4];}\\
{\tt DATA} \= {\tt = 0;} \\
{\tt CMD} \> {\tt = (SD\_FIFO\_OP|SD\_CMD)+9;}
{\tt CMD} \> {\tt = (SD\_FIFO\_OP|SD\_CMD)+9;} \\
{\tt SD\_WAIT\_WHILE\_BUSY;} \\
{\tt for(int i=0; i<4; i++) } \\
\> {\tt CSD[i] = FIFO[0];}
/trunk/rtl/Makefile
1,12 → 1,53
################################################################################
##
## Filename: Makefile
##
## Project: SD-Card controller, using a shared SPI interface
##
## Purpose: To build the Verilator library necessary for simulating (via
## verilator) this code.
##
## Creator: Dan Gisselquist, Ph.D.
## Gisselquist Technology, LLC
##
################################################################################
##
## Copyright (C) 2016, Gisselquist Technology, LLC
##
## This program is free software (firmware): 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 3 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 MERCHANTIBILITY 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. (It's in the $(ROOT)/doc directory, run make with no
## target there if the PDF file isn't present.) If not, see
## <http://www.gnu.org/licenses/> for a copy.
##
## License: GPL, v3, as defined and found on www.gnu.org,
## http://www.gnu.org/licenses/gpl.html
##
##
################################################################################
##
##
 
all: rtl export.v
VERILATOR:= verilator
# VOB := $(HOME)/src/verilog/vopro
VOB := cat
VOBJ:= obj_dir
VFLAGS := -MMD -Wall -trace -cc
 
.PHONY: rtl
rtl: $(VOBJ)/Vsdspi.h
$(VOBJ)/Vsdspi.h: sdspi.v llsdspi.v
verilator -cc sdspi.v
$(VERILATOR) $(VFLAGS) sdspi.v
 
export.v: sdspi.v llsdspi.v
$(VOB) $^ > $@
/trunk/rtl/llsdspi.v
87,6 → 87,8
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
//
`define LLSDSPI_IDLE 4'h0
`define LLSDSPI_HOTIDLE 4'h1
`define LLSDSPI_WAIT 4'h2
97,22 → 99,22
o_stb, o_byte, o_idle, i_bus_grant);
parameter SPDBITS = 7;
//
input i_clk;
input wire i_clk;
// Parameters/setup
input [(SPDBITS-1):0] i_speed;
input wire [(SPDBITS-1):0] i_speed;
// The incoming interface
input i_cs;
input i_stb;
input [7:0] i_byte;
input wire i_cs;
input wire i_stb;
input wire [7:0] i_byte;
// The actual SPI interface
output reg o_cs_n, o_sclk, o_mosi;
input i_miso;
input wire i_miso;
// The outgoing interface
output reg o_stb;
output reg [7:0] o_byte;
output wire o_idle;
// And whether or not we actually own the interface (yet)
input i_bus_grant;
input wire i_bus_grant;
 
reg r_z_counter;
reg [(SPDBITS-1):0] r_clk_counter;
126,11 → 128,11
initial r_clk_counter = 7'h0;
always @(posedge i_clk)
begin
if ((~i_cs)||(~i_bus_grant))
if ((!i_cs)||(!i_bus_grant))
r_clk_counter <= 0;
else if (byte_accepted)
r_clk_counter <= i_speed;
else if (~r_z_counter)
else if (!r_z_counter)
r_clk_counter <= (r_clk_counter - {{(SPDBITS-1){1'b0}},1'b1});
else if ((r_state != `LLSDSPI_IDLE)&&(r_state != `LLSDSPI_HOTIDLE))
r_clk_counter <= (i_speed);
141,11 → 143,11
initial r_z_counter = 1'b1;
always @(posedge i_clk)
begin
if ((~i_cs)||(~i_bus_grant))
if ((!i_cs)||(!i_bus_grant))
r_z_counter <= 1'b1;
else if (byte_accepted)
r_z_counter <= 1'b0;
else if (~r_z_counter)
else if (!r_z_counter)
r_z_counter <= (r_clk_counter == 1);
else if ((r_state != `LLSDSPI_IDLE)&&(r_state != `LLSDSPI_HOTIDLE))
r_z_counter <= 1'b0;
155,13 → 157,13
always @(posedge i_clk)
begin
o_stb <= 1'b0;
o_cs_n <= ~i_cs;
if (~i_cs)
o_cs_n <= !i_cs;
if (!i_cs)
begin
r_state <= `LLSDSPI_IDLE;
r_idle <= 1'b0;
o_sclk <= 1'b1;
end else if (~r_z_counter)
end else if (!r_z_counter)
begin
r_idle <= 1'b0;
if (byte_accepted)
/trunk/rtl/sdspi.v
11,7 → 11,7
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2016, Gisselquist Technology, LLC
// Copyright (C) 2016-2017, Gisselquist Technology, LLC
//
// This program is free software (firmware): you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
24,7 → 24,7
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program. (It's in the $(ROOT)/doc directory, run make with no
// with this program. (It's in the $(ROOT)/doc directory. Run make with no
// target there if the PDF file isn't present.) If not, see
// <http://www.gnu.org/licenses/> for a copy.
//
35,6 → 35,8
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
//
`define SDSPI_CMD_ADDRESS 2'h0
`define SDSPI_DAT_ADDRESS 2'h1
`define SDSPI_FIFO_A_ADDR 2'h2
74,21 → 76,21
// And some wires for debugging it all
o_debug);
parameter LGFIFOLN = 7;
input i_clk;
input wire i_clk;
//
input i_wb_cyc, i_wb_stb, i_wb_we;
input [1:0] i_wb_addr;
input [31:0] i_wb_data;
input wire i_wb_cyc, i_wb_stb, i_wb_we;
input wire [1:0] i_wb_addr;
input wire [31:0] i_wb_data;
output reg o_wb_ack;
output wire o_wb_stall;
output reg [31:0] o_wb_data;
//
output wire o_cs_n, o_sck, o_mosi;
input i_miso;
input wire i_miso;
// The interrupt
output reg o_int;
// .. and whether or not we can use the SPI port
input i_bus_grant;
input wire i_bus_grant;
//
output wire [31:0] o_debug;
 
96,14 → 98,51
// Some WB simplifications:
//
reg r_cmd_busy;
 
wire wb_stb, write_stb, cmd_stb, new_data, new_cmd;
wire [1:0] wb_addr;
wire [31:0] wb_data;
`ifdef WB_CLOCK
wire wb_stb, write_stb, cmd_stb; // read_stb
assign wb_stb = ((i_wb_cyc)&&(i_wb_stb)&&(~o_wb_stall));
assign wb_stb = ((i_wb_stb)&&(!o_wb_stall));
assign write_stb = ((wb_stb)&&( i_wb_we));
// assign read_stb = ((wb_stb)&&(~i_wb_we));
assign cmd_stb = (~r_cmd_busy)&&(write_stb)
// assign read_stb = ((wb_stb)&&(!i_wb_we));
assign cmd_stb = (!r_cmd_busy)&&(write_stb)
&&(i_wb_addr==`SDSPI_CMD_ADDRESS);
assign wb_addr = i_wb_addr;
assign wb_data = i_wb_data;
assign new_cmd = cmd_stb;
assign new_data = (i_wb_stb)&&(!o_wb_stall)&&(i_wb_we)
&&(i_wb_addr == `SDSPI_DAT_ADDRESS);
`else
reg r_wb_stb, r_write_stb, r_cmd_stb, r_new_data;
reg [1:0] r_wb_addr;
reg [31:0] r_wb_data;
always @(posedge i_clk)
r_wb_stb <= ((i_wb_stb)&&(!o_wb_stall));
always @(posedge i_clk)
r_write_stb <= ((i_wb_stb)&&(!o_wb_stall)&&(i_wb_we));
always @(posedge i_clk)
r_cmd_stb <= (!r_cmd_busy)&&(i_wb_stb)&&(!o_wb_stall)&&(i_wb_we)
&&(i_wb_addr == `SDSPI_CMD_ADDRESS);
always @(posedge i_clk)
r_new_data <= (i_wb_stb)&&(!o_wb_stall)&&(i_wb_we)
&&(i_wb_addr == `SDSPI_DAT_ADDRESS);
always @(posedge i_clk)
r_wb_addr <= i_wb_addr;
always @(posedge i_clk)
r_wb_data <= i_wb_data;
 
assign wb_stb = r_wb_stb;
assign write_stb= r_write_stb;
assign cmd_stb = r_cmd_stb;
assign new_cmd = r_cmd_stb;
assign new_data = r_new_data;
assign wb_addr = r_wb_addr;
assign wb_data = r_wb_data;
`endif
 
 
//
// Access to our lower-level SDSPI driver, the one that actually
// uses/sets the SPI ports
130,7 → 169,10
r_fifo_id,
ll_fifo_wr, ll_fifo_rd,
r_have_data_response_token,
r_have_start_token;
r_have_start_token,
r_err_token;
reg [3:0] r_read_err_token;
reg [1:0] r_data_response_token;
reg [7:0] fifo_byte;
reg [7:0] r_last_r_one;
//
145,11 → 187,15
//
reg q_busy;
//
reg [7:0] fifo_a_mem[((1<<(LGFIFOLN+2))-1):0];
reg [7:0] fifo_b_mem[((1<<(LGFIFOLN+2))-1):0];
reg [7:0] fifo_a_mem_0[0:((1<<LGFIFOLN)-1)],
fifo_a_mem_1[0:((1<<LGFIFOLN)-1)],
fifo_a_mem_2[0:((1<<LGFIFOLN)-1)],
fifo_a_mem_3[0:((1<<LGFIFOLN)-1)],
fifo_b_mem_0[0:((1<<LGFIFOLN)-1)],
fifo_b_mem_1[0:((1<<LGFIFOLN)-1)],
fifo_b_mem_2[0:((1<<LGFIFOLN)-1)],
fifo_b_mem_3[0:((1<<LGFIFOLN)-1)];
reg [(LGFIFOLN-1):0] fifo_wb_addr;
reg [(LGFIFOLN+1):0] rd_fifo_sd_addr;
reg [(LGFIFOLN+1):0] wr_fifo_sd_addr;
//
reg [(LGFIFOLN+1):0] ll_fifo_addr;
//
176,6 → 222,8
reg r_watchdog_err;
reg pre_cmd_state;
 
// Relieve some stress from the WB bus timing
 
initial r_cmd_busy = 1'b0;
initial r_data_reg = 32'h00;
initial r_last_r_one = 8'hff;
190,7 → 238,7
initial r_cmd_err = 1'b0;
always @(posedge i_clk)
begin
if (~ll_cmd_stb)
if (!ll_cmd_stb)
begin
r_have_resp <= 1'b0;
ll_fifo_wr <= 1'b0;
273,9 → 321,9
// are expecting.
if (pre_rsp_state)
begin
if (r_rsp_state == `SDSPI_RSP_NONE)
begin // Waiting on R1
if (~ll_out_dat[7])
case(r_rsp_state)
`SDSPI_RSP_NONE: begin // Waiting on R1
if (!ll_out_dat[7])
begin
r_last_r_one <= ll_out_dat;
if (r_cmd_resp == `SDSPI_EXPECT_R1)
283,28 → 331,30
r_have_resp <= 1'b1;
ll_cmd_stb <= (r_use_fifo);
r_data_reg <= 32'hffffffff;
ll_fifo_wr<=(r_use_fifo)&&(~r_fifo_wr);
ll_fifo_wr<=(r_use_fifo)&&(!r_fifo_wr);
end else if (r_cmd_resp == `SDSPI_EXPECT_R1B)
begin // Go wait on R1b
r_data_reg <= 32'hffffffff;
end // else wait on 32-bit rsp
end
end else if (r_rsp_state == `SDSPI_RSP_BSYWAIT)
begin // Waiting on R1b, have R1
end end
`SDSPI_RSP_BSYWAIT: begin
// Waiting on R1b, have R1
if (nonzero_out)
r_have_resp <= 1'b1;
ll_cmd_stb <= (r_use_fifo);
end else if (r_rsp_state == `SDSPI_RSP_GETWORD)
begin // Have R1, waiting on all of R2/R3/R7
r_data_reg <= { r_data_reg[23:0], ll_out_dat };
end
`SDSPI_RSP_GETWORD: begin
// Have R1, waiting on all of R2/R3/R7
r_data_reg <= { r_data_reg[23:0],
ll_out_dat };
r_data_fil <= r_data_fil+2'b01;
if (r_data_fil == 2'b11)
begin
ll_cmd_stb <= (r_use_fifo);
// r_rsp_state <= 3'h3;
end
end else if (r_rsp_state == `SDSPI_RSP_WAIT_WHILE_BUSY)
begin // Wait while device is busy writing
end end
`SDSPI_RSP_WAIT_WHILE_BUSY: begin
// Wait while device is busy writing
// if (nonzero_out)
// begin
// r_data_reg[31:8] <= 24'h00;
311,63 → 361,68
// r_data_reg[7:0] <= ll_out_dat;
// // r_rsp_state <= 3'h6;
// end
;
end else if (r_rsp_state == `SDSPI_RSP_RDCOMPLETE)
begin // Block write command has completed
end
`SDSPI_RSP_RDCOMPLETE: begin
// Block write command has completed
ll_cmd_stb <= 1'b0;
end else if (r_rsp_state == `SDSPI_RSP_WRITING)
begin // We are reading from the device into
end
`SDSPI_RSP_WRITING: begin
// We are reading from the device into
// our FIFO
if ((ll_fifo_wr_complete)
// Or ... we receive an error
||((~r_have_start_token)
&&(~ll_out_dat[4])
&&(ll_out_dat[0])))
||(r_read_err_token[0]))
begin
ll_fifo_wr <= 1'b0;
ll_cmd_stb <= 1'b0;
end
end
end end
// `SDSPI_RSP_GETTOKEN:
default: begin end
endcase
end
 
if ((r_use_fifo)&&(ll_out_stb))
r_data_reg <= { 26'h3ffffff, r_data_response_token, r_read_err_token };
 
if (r_watchdog_err)
ll_cmd_stb <= 1'b0;
r_cmd_err<= (r_cmd_err)|(fifo_crc_err)|(r_watchdog_err);
r_cmd_err<= (r_cmd_err)|(fifo_crc_err)|(r_watchdog_err)
|(r_err_token);
end else if (r_cmd_busy)
begin
r_cmd_busy <= (ll_cmd_stb)||(~ll_idle);
end else if (cmd_stb)
r_cmd_busy <= (ll_cmd_stb)||(!ll_idle);
end else if (new_cmd)
begin // Command write
// Clear the error on any write, whether a commanding
// one or not. -- provided the user requests clearing
// it (by setting the bit high)
r_cmd_err <= (r_cmd_err)&&(~i_wb_data[15]);
r_cmd_err <= (r_cmd_err)&&(!wb_data[15]);
// In a similar fashion, we can switch fifos even if
// not in the middle of a command
r_fifo_id <= i_wb_data[12];
r_fifo_id <= wb_data[12];
//
// Doesn't matter what this is set to as long as we
// aren't busy, so we can set it irrelevantly here.
ll_cmd_dat <= i_wb_data[7:0];
ll_cmd_dat <= wb_data[7:0];
//
// Note that we only issue a write upon receiving a
// valid command. Such a command is 8 bits, and must
// start with its high order bits set to zero and one.
// Hence ... we test for that here.
if (i_wb_data[7:6] == 2'b01)
if (wb_data[7:6] == 2'b01)
begin // Issue a command
//
r_cmd_busy <= 1'b1;
//
ll_cmd_stb <= 1'b1;
r_cmd_resp <= i_wb_data[9:8];
r_cmd_resp <= wb_data[9:8];
//
r_cmd_crc_stb <= 1'b1;
//
r_fifo_wr <= i_wb_data[10];
r_use_fifo <= i_wb_data[11];
r_fifo_wr <= wb_data[10];
r_use_fifo <= wb_data[11];
//
end else if (i_wb_data[7])
end else if (wb_data[7])
// If, on the other hand, the command was invalid,
// then it must have been an attempt to read our
// internal configuration. So we'll place that on
375,10 → 430,8
r_data_reg <= { 8'h00,
4'h0, max_lgblklen,
4'h0, r_lgblklen, 1'b0, r_sdspi_clk };
end else if ((write_stb)&&(i_wb_addr == `SDSPI_DAT_ADDRESS))
begin // Data write
r_data_reg <= i_wb_data;
end
end else if (new_data) // Data write
r_data_reg <= wb_data;
end
 
always @(posedge i_clk)
386,14 → 439,14
 
reg ready_for_response_token;
always @(posedge i_clk)
if (~r_cmd_busy)
if (!r_cmd_busy)
ready_for_response_token <= 1'b0;
else if (ll_fifo_rd)
ready_for_response_token <= 1'b1;
always @(posedge i_clk)
if (~r_cmd_busy)
if (!r_cmd_busy)
r_have_data_response_token <= 1'b0;
else if ((ll_out_stb)&&(ready_for_response_token)&&(~ll_out_dat[4]))
else if ((ll_out_stb)&&(ready_for_response_token)&&(!ll_out_dat[4]))
r_have_data_response_token <= 1'b1;
 
reg [2:0] second_rsp_state;
417,11 → 470,11
// Each bit depends upon 8 bits of input
initial r_rsp_state = 3'h0;
always @(posedge i_clk)
if (~r_cmd_sent)
if (!r_cmd_sent)
r_rsp_state <= 3'h0;
else if (pre_rsp_state)
begin
if ((r_rsp_state == `SDSPI_RSP_NONE)&&(~ll_out_dat[7]))
if ((r_rsp_state == `SDSPI_RSP_NONE)&&(!ll_out_dat[7]))
begin
r_rsp_state <= second_rsp_state;
end else if (r_rsp_state == `SDSPI_RSP_BSYWAIT)
462,7 → 515,7
// with the card. These include the speed of the interface,
// and the size of the block length to expect as part of a FIFO
// command.
if ((cmd_stb)&&(i_wb_data[7:6]==2'b11)&&(~r_data_reg[7])
if ((new_cmd)&&(wb_data[7:6]==2'b11)&&(!r_data_reg[7])
&&(r_data_reg[15:12]==4'h00))
begin
if (|r_data_reg[6:0])
475,10 → 528,10
 
assign need_reset = 1'b0;
always @(posedge i_clk)
case(i_wb_addr)
case(wb_addr)
`SDSPI_CMD_ADDRESS:
o_wb_data <= { need_reset, 11'h00,
3'h0, fifo_crc_err,
2'h0, r_err_token, fifo_crc_err,
r_cmd_err, r_cmd_busy, 1'b0, r_fifo_id,
r_use_fifo, r_fifo_wr, r_cmd_resp,
r_last_r_one };
497,7 → 550,7
always @(posedge i_clk)
q_busy <= r_cmd_busy;
always @(posedge i_clk)
o_int <= (~r_cmd_busy)&&(q_busy);
o_int <= (!r_cmd_busy)&&(q_busy);
 
assign o_wb_stall = 1'b0;
 
507,11 → 560,11
//
always @(posedge i_clk)
begin
if ((write_stb)&&(i_wb_addr == `SDSPI_CMD_ADDRESS))
if ((write_stb)&&(wb_addr == `SDSPI_CMD_ADDRESS))
begin // Command write
// Clear the read/write address
fifo_wb_addr <= {(LGFIFOLN){1'b0}};
end else if ((wb_stb)&&(i_wb_addr[1]))
end else if ((wb_stb)&&(wb_addr[1]))
begin // On read or write, of either FIFO,
// we increase our pointer
fifo_wb_addr <= fifo_wb_addr + 1;
525,15 → 578,15
always @(posedge i_clk)
begin
fifo_a_reg <= {
fifo_a_mem[{ fifo_wb_addr, 2'b00 }],
fifo_a_mem[{ fifo_wb_addr, 2'b01 }],
fifo_a_mem[{ fifo_wb_addr, 2'b10 }],
fifo_a_mem[{ fifo_wb_addr, 2'b11 }] };
fifo_a_mem_0[ fifo_wb_addr ],
fifo_a_mem_1[ fifo_wb_addr ],
fifo_a_mem_2[ fifo_wb_addr ],
fifo_a_mem_3[ fifo_wb_addr ] };
fifo_b_reg <= {
fifo_b_mem[{ fifo_wb_addr, 2'b00 }],
fifo_b_mem[{ fifo_wb_addr, 2'b01 }],
fifo_b_mem[{ fifo_wb_addr, 2'b10 }],
fifo_b_mem[{ fifo_wb_addr, 2'b11 }] };
fifo_b_mem_0[ fifo_wb_addr ],
fifo_b_mem_1[ fifo_wb_addr ],
fifo_b_mem_2[ fifo_wb_addr ],
fifo_b_mem_3[ fifo_wb_addr ] };
end
 
// Okay, now ... writing our FIFO ...
542,25 → 595,50
initial pre_fifo_addr_inc_rd = 1'b0;
initial pre_fifo_addr_inc_wr = 1'b0;
always @(posedge i_clk)
pre_fifo_addr_inc_wr <= ((ll_fifo_wr)&&(ll_out_stb)&&(r_have_start_token));
pre_fifo_addr_inc_wr <= ((ll_fifo_wr)&&(ll_out_stb)
&&(r_have_start_token));
always @(posedge i_clk)
pre_fifo_addr_inc_rd <= ((ll_fifo_rd)&&(ll_cmd_stb)&&(ll_idle));//&&(ll_fifo_pkt_state[2:0]!=3'b000));
pre_fifo_addr_inc_rd <= ((ll_fifo_rd)&&(ll_cmd_stb)&&(ll_idle));
always @(posedge i_clk)
begin
// if ((write_stb)&&(i_wb_addr == `SDSPI_CMD_ADDRESS)&&(i_wb_data[11]))
// ll_fifo_addr <= {(LGFIFOLN+2){1'b0}};
if (~r_cmd_busy)
if (!r_cmd_busy)
ll_fifo_addr <= {(LGFIFOLN+2){1'b0}};
else if ((pre_fifo_addr_inc_wr)||(pre_fifo_addr_inc_rd))
ll_fifo_addr <= ll_fifo_addr + 1;
end
 
// Look for that start token
//
// Look for that start token. This will be present when reading from
// the device into the FIFO.
//
always @(posedge i_clk)
if (~r_cmd_busy)
if (!r_cmd_busy)
r_have_start_token <= 1'b0;
else if ((ll_fifo_wr)&&(ll_out_stb)&&(ll_out_dat==8'hfe))
r_have_start_token <= 1'b1;
always @(posedge i_clk)
if (!r_cmd_busy)
r_read_err_token <= 4'h0;
else if ((ll_fifo_wr)&&(ll_out_stb)&&(!r_have_start_token)
&&(ll_out_dat[7:4]==4'h0))
r_read_err_token <= ll_out_dat[3:0];
always @(posedge i_clk) // Look for a response to our writing
if (!r_cmd_busy)
r_data_response_token <= 2'b00;
else if ((ready_for_response_token)
&&(!ll_out_dat[4])&&(ll_out_dat[0]))
r_data_response_token <= ll_out_dat[3:2];
initial r_err_token = 1'b0;
always @(posedge i_clk)
if (ll_fifo_rd)
r_err_token <= (r_err_token)|(r_read_err_token[0]);
else if (ll_fifo_wr)
r_err_token <= (r_err_token)|
((|r_data_response_token)&&(r_data_response_token[1]));
else if (cmd_stb)
// Clear the error on any write with the bit high
r_err_token <= (r_err_token)&&(!i_wb_data[16])
&&(!i_wb_data[15]);
 
reg last_fifo_byte;
initial last_fifo_byte = 1'b0;
582,39 → 660,89
clear_fifo_crc;
always @(posedge i_clk)
begin
pre_fifo_a_wr <= (ll_fifo_wr)&&(ll_out_stb)&&(~r_fifo_id)&&(ll_fifo_wr_state == 2'b00);
pre_fifo_b_wr <= (ll_fifo_wr)&&(ll_out_stb)&&( r_fifo_id)&&(ll_fifo_wr_state == 2'b00);
fifo_wr_crc_stb <= (ll_fifo_wr)&&(ll_out_stb)&&(ll_fifo_wr_state == 2'b00)&&(r_have_start_token);
pre_fifo_crc_a<= (ll_fifo_wr)&&(ll_out_stb)&&(ll_fifo_wr_state == 2'b01);
pre_fifo_crc_b<= (ll_fifo_wr)&&(ll_out_stb)&&(ll_fifo_wr_state == 2'b10);
clear_fifo_crc <= (cmd_stb)&&(i_wb_data[15]);
pre_fifo_a_wr <= (ll_fifo_wr)&&(ll_out_stb)
&&(!r_fifo_id)&&(ll_fifo_wr_state == 2'b00);
pre_fifo_b_wr <= (ll_fifo_wr)&&(ll_out_stb)
&&( r_fifo_id)&&(ll_fifo_wr_state == 2'b00);
fifo_wr_crc_stb <= (ll_fifo_wr)&&(ll_out_stb)
&&(ll_fifo_wr_state == 2'b00)&&(r_have_start_token);
pre_fifo_crc_a<= (ll_fifo_wr)&&(ll_out_stb)
&&(ll_fifo_wr_state == 2'b01);
pre_fifo_crc_b<= (ll_fifo_wr)&&(ll_out_stb)
&&(ll_fifo_wr_state == 2'b10);
clear_fifo_crc <= (new_cmd)&&(wb_data[15]);
end
 
reg fifo_a_wr, fifo_b_wr;
reg [3:0] fifo_a_wr_mask, fifo_b_wr_mask;
reg [(LGFIFOLN-1):0] fifo_a_wr_addr, fifo_b_wr_addr;
reg [31:0] fifo_a_wr_data, fifo_b_wr_data;
 
initial fifo_crc_err = 1'b0;
always @(posedge i_clk)
begin // One and only memory write allowed
if ((write_stb)&&(i_wb_addr[1:0]==2'b10))
{fifo_a_mem[{ fifo_wb_addr, 2'b00 }],
fifo_a_mem[{ fifo_wb_addr, 2'b01 }],
fifo_a_mem[{ fifo_wb_addr, 2'b10 }],
fifo_a_mem[{ fifo_wb_addr, 2'b11 }] }
<= i_wb_data;
else if (pre_fifo_a_wr)
fifo_a_mem[{ ll_fifo_addr }] <= ll_out_dat;
fifo_a_wr <= 1'b0;
fifo_a_wr_data <= { ll_out_dat, ll_out_dat, ll_out_dat, ll_out_dat };
if ((write_stb)&&(wb_addr[1:0]==2'b10))
begin
fifo_a_wr <= 1'b1;
fifo_a_wr_mask <= 4'b1111;
fifo_a_wr_addr <= fifo_wb_addr;
fifo_a_wr_data <= wb_data;
end else if (pre_fifo_a_wr)
begin
fifo_a_wr <= 1'b1;
fifo_a_wr_addr <= ll_fifo_addr[(LGFIFOLN+1):2];
case(ll_fifo_addr[1:0])
2'b00: fifo_a_wr_mask <= 4'b0001;
2'b01: fifo_a_wr_mask <= 4'b0010;
2'b10: fifo_a_wr_mask <= 4'b0100;
2'b11: fifo_a_wr_mask <= 4'b1000;
endcase
end
 
if ((write_stb)&&(i_wb_addr[1:0]==2'b11))
{fifo_b_mem[{fifo_wb_addr, 2'b00 }],
fifo_b_mem[{ fifo_wb_addr, 2'b01 }],
fifo_b_mem[{ fifo_wb_addr, 2'b10 }],
fifo_b_mem[{ fifo_wb_addr, 2'b11 }] }
<= i_wb_data;
else if (pre_fifo_b_wr)
fifo_b_mem[{ ll_fifo_addr }] <= ll_out_dat;
if ((fifo_a_wr)&&(fifo_a_wr_mask[0]))
fifo_a_mem_0[fifo_a_wr_addr] <= fifo_a_wr_data[7:0];
if ((fifo_a_wr)&&(fifo_a_wr_mask[1]))
fifo_a_mem_1[fifo_a_wr_addr] <= fifo_a_wr_data[15:8];
if ((fifo_a_wr)&&(fifo_a_wr_mask[2]))
fifo_a_mem_2[fifo_a_wr_addr] <= fifo_a_wr_data[23:16];
if ((fifo_a_wr)&&(fifo_a_wr_mask[3]))
fifo_a_mem_3[fifo_a_wr_addr] <= fifo_a_wr_data[31:24];
 
if (~r_cmd_busy)
fifo_b_wr <= 1'b0;
fifo_b_wr_data <= { ll_out_dat, ll_out_dat, ll_out_dat, ll_out_dat };
if ((write_stb)&&(wb_addr[1:0]==2'b11))
begin
fifo_b_wr <= 1'b1;
fifo_b_wr_mask <= 4'b1111;
fifo_b_wr_addr <= fifo_wb_addr;
fifo_b_wr_data <= wb_data;
end else if (pre_fifo_b_wr)
begin
fifo_b_wr <= 1'b1;
fifo_b_wr_addr <= ll_fifo_addr[(LGFIFOLN+1):2];
case(ll_fifo_addr[1:0])
2'b00: fifo_b_wr_mask <= 4'b0001;
2'b01: fifo_b_wr_mask <= 4'b0010;
2'b10: fifo_b_wr_mask <= 4'b0100;
2'b11: fifo_b_wr_mask <= 4'b1000;
endcase
end
 
if ((fifo_b_wr)&&(fifo_b_wr_mask[0]))
fifo_b_mem_0[fifo_b_wr_addr] <= fifo_b_wr_data[7:0];
if ((fifo_b_wr)&&(fifo_b_wr_mask[1]))
fifo_b_mem_1[fifo_b_wr_addr] <= fifo_b_wr_data[15:8];
if ((fifo_b_wr)&&(fifo_b_wr_mask[2]))
fifo_b_mem_2[fifo_b_wr_addr] <= fifo_b_wr_data[23:16];
if ((fifo_b_wr)&&(fifo_b_wr_mask[3]))
fifo_b_mem_3[fifo_b_wr_addr] <= fifo_b_wr_data[31:24];
 
if (!r_cmd_busy)
ll_fifo_wr_complete <= 1'b0;
 
if (~r_cmd_busy)
if (!r_cmd_busy)
ll_fifo_wr_state <= 2'b00;
else if ((pre_fifo_a_wr)||(pre_fifo_b_wr))
ll_fifo_wr_state <= (last_fifo_byte)? 2'b01:2'b00;
634,14 → 762,30
 
always @(posedge i_clk)
begin // Second memory read, this time for the FIFO
fifo_a_byte <= fifo_a_mem[ ll_fifo_addr ];
fifo_b_byte <= fifo_b_mem[ ll_fifo_addr ];
case(ll_fifo_addr[1:0])
2'b00: begin
fifo_a_byte<=fifo_a_mem_0[ll_fifo_addr[(LGFIFOLN+1):2]];
fifo_b_byte<=fifo_b_mem_0[ll_fifo_addr[(LGFIFOLN+1):2]];
end
2'b01: begin
fifo_a_byte<=fifo_a_mem_1[ll_fifo_addr[(LGFIFOLN+1):2]];
fifo_b_byte<=fifo_b_mem_1[ll_fifo_addr[(LGFIFOLN+1):2]];
end
2'b10: begin
fifo_a_byte<=fifo_a_mem_2[ll_fifo_addr[(LGFIFOLN+1):2]];
fifo_b_byte<=fifo_b_mem_2[ll_fifo_addr[(LGFIFOLN+1):2]];
end
2'b11: begin
fifo_a_byte<=fifo_a_mem_3[ll_fifo_addr[(LGFIFOLN+1):2]];
fifo_b_byte<=fifo_b_mem_3[ll_fifo_addr[(LGFIFOLN+1):2]];
end
endcase
end
 
reg [(LGFIFOLN-1):0] r_blklimit;
wire [(LGFIFOLN+1):0] w_blklimit;
always @(posedge i_clk)
r_blklimit[(LGFIFOLN-1):0] = (1<<r_lgblklen)-1;
r_blklimit[(LGFIFOLN-1):0] <= (1<<r_lgblklen)-1;
assign w_blklimit = { r_blklimit, 2'b11 };
 
// Package the FIFO reads up into a packet
695,11 → 839,11
ll_fifo_rd_complete <= 1'b1;
fifo_byte <= 8'hff;
end
end else if ((write_stb)&&(i_wb_addr == `SDSPI_CMD_ADDRESS))
end else if ((write_stb)&&(wb_addr == `SDSPI_CMD_ADDRESS))
begin
ll_fifo_pkt_state <= 3'h0;
ll_fifo_rd_complete <= 1'b0;
fifo_byte <= (i_wb_data[12]) ? fifo_b_byte : fifo_a_byte;
fifo_byte <= (wb_data[12]) ? fifo_b_byte : fifo_a_byte;
fifo_rd_crc_stb <= 1'b1;
end else begin // Packet state is IDLE (clear the CRC registers)
ll_fifo_pkt_state <= 3'b111;
709,7 → 853,7
 
always @(posedge i_clk)
begin
if (~ll_fifo_wr)
if (!ll_fifo_wr)
fifo_wr_crc_reg <= 16'h00;
else if (fifo_wr_crc_stb)
begin
728,7 → 872,7
 
always @(posedge i_clk)
begin
if (~r_cmd_busy)
if (!r_cmd_busy)
begin
fifo_rd_crc_reg <= 16'h00;
fifo_rd_crc_count <= 4'h0;
753,12 → 897,12
initial r_cmd_crc_ff = 1'b0;
always @(posedge i_clk)
begin
if (~r_cmd_busy)
if (!r_cmd_busy)
begin
r_cmd_crc <= 8'h00;
r_cmd_crc_cnt <= 4'hf;
r_cmd_crc_ff <= 1'b0;
end else if (~r_cmd_crc_cnt[3])
end else if (!r_cmd_crc_cnt[3])
begin
r_cmd_crc_cnt <= r_cmd_crc_cnt - 4'h1;
if (r_cmd_crc[7])
784,12 → 928,12
initial r_watchdog = 26'h3ffffff;
initial r_watchdog_err = 1'b0;
always @(posedge i_clk)
if (~r_cmd_busy)
if (!r_cmd_busy)
r_watchdog_err <= 1'b0;
else if (r_watchdog == 0)
r_watchdog_err <= 1'b1;
always @(posedge i_clk)
if (~r_cmd_busy)
if (!r_cmd_busy)
r_watchdog <= 26'h3fffff;
else if (|r_watchdog)
r_watchdog <= r_watchdog - 26'h1;
801,5 → 945,11
r_rsp_state, r_cmd_busy, // 4'h
ll_cmd_dat, // 8'b
ll_out_dat }; // 8'b
 
// Make verilator happy
// verilator lint_off UNUSED
wire unused;
assign unused = i_wb_cyc;
// verilator lint_on UNUSED
endmodule
 

powered by: WebSVN 2.1.0

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