// part of NeoGS project
 
//
 
// (c) NedoPC 2007-2009
 
//
 
// SD-card dma controller
 
//
 
// includes dma address regs, dma control reg
 
/*
 
 
 
 Read from sd-card modes:
 
 
 
  1. Full burst: first all 512 bytes are read into 512b buffer, then dma-bursted into main memory.
 
     Not as fast in latency, but steals minimum of CPU cycles, though stops cpu for 1024+ clocks (if no other DMAs are active)
 
 
 
  2. As soon as possible: initiates DMA as soon as new byte is arrived into 512b buffer.
 
     Makes bursts of 2-3 bytes, when applicable (more bytes arriven since beginning of dma_req up to acknowledge in dma_ack)
 
 
 
 Write to sd-card modes:
 
 
 
  1. Full burst: all 512 bytes are read in one burst, transmission starts as soon as first byte arrives to the buffer.
 
     Uses minimum of CPU cycles (1024+) but in one chunk.
 
 
 
  2. DMA initiated as soon as spi is again ready to initiate new transfer (kind a throttling). Probably DMA pulls 2 or 4 bytes at once.
 
 
 
Structure:
 
 
 
 - FIFO based on mem512b and two pointers
 
 
 
 - SD controller - FSM which either reads or writes SD-card SPI iface
 
 
 
 - DMA controller - FSM which either reads or writes DMA iface
 
 
 
 - overall control - Controls operation of everything above, controls muxing of data to the
 
   SD write port and FIFO write port, tracks operation end, maintains DMA_ADDRESS registers.
 
 
 
*/
 
 
 
 
 
module dma_sd(
 
 
 
        input  wire clk,
 
        input  wire rst_n,
 
 
 
        // control to spi module of SD-card
 
        //
 
        output reg        sd_start,
 
        input  wire       sd_rdy,
 
        input  wire [7:0] sd_receiveddata,
 
        output wire [7:0] sd_datatosend,
 
        //
 
        output reg        sd_override, // when 1, override sd_start and sd_datatosend to the sd spi module
 
 
 
 
 
 
 
        // signals for ports.v
 
        //
 
        input  wire [7:0] din,  // input and output from ports.v
 
        output reg  [7:0] dout,
 
        //
 
        input  wire       module_select, // =1 - module selected for read-write operations from ports.v
 
        input  wire       write_strobe,  // one-cycle positive write strobe - writes to the selected registers from din
 
        //
 
        input  wire [1:0] regsel, // 2'b00 - high address, 2'b01 - middle address, 2'b10 - low address, 2'b11 - control register
 
 
 
        // signals for DMA controller/DMA sequencer
 
        //
 
        output reg  [20:0] dma_addr,
 
        output wire  [7:0] dma_wd,   // data written to DMA
 
        input  wire  [7:0] dma_rd,   // data read from DMA
 
        output reg         dma_rnw,
 
        //
 
        output wire        dma_req,
 
        input  wire        dma_ack,
 
        input  wire        dma_end
 
);
 
        localparam _HAD = 2'b00; // high address
 
        localparam _MAD = 2'b01; // mid address
 
        localparam _LAD = 2'b10; // low address
 
        localparam _CST = 2'b11; // control and status
 
 
 
 
 
        reg dma_snr;   // Send-NotReceive. Send to SDcard (==1) or Receive (==0) from it.
 
        reg dma_burst; // whether burst transfers are on
 
 
 
 
 
 
 
 
 
 
 
        // control dout bus
 
        always @*
 
        case( regsel[1:0] )
 
                _HAD: dout = { 3'b000, dma_addr[20:16] };
 
                _MAD: dout =           dma_addr[15:8];
 
                _LAD: dout =           dma_addr[7:0];
 
                _CST: dout = { dma_on, 5'bXXXXX, dma_burst, dma_snr};
 
        endcase
 
 
 
        // ports.v write access & dma_addr control
 
        always @(posedge clk, negedge rst_n)
 
        if( !rst_n ) // async reset
 
        begin
 
                dma_on    <= 1'b0;
 
                dma_snr   <= 1'b0; // receive is less dangerous since it won't destroy SDcard info
 
                dma_burst <= 1'b0;
 
        end
 
        else // posedge clk
 
        begin
 
                // dma_on control
 
                if( module_select && write_strobe && (regsel==_CST) )
 
                begin
 
                        dma_on    <= din[7];
 
                        dma_burst <= din[1];
 
                        dma_snr   <= din[0];
 
                end
 
                else if( dma_finish )
 
                begin
 
                        dma_on <= 1'b0;
 
                end
 
 
 
                // dma_addr control
 
                if( dma_ack && dma_on )
 
                        dma_addr <= dma_addr + 21'd1; // increment on beginning of DMA transfer
 
                else if( module_select && write_strobe )
 
                begin
 
                        if( regsel==_HAD )
 
                                dma_addr[20:16] <= din[4:0];
 
                        else if( regsel==_MAD )
 
                                dma_addr[15:8]  <= din[7:0];
 
                        else if( regsel==_LAD )
 
                                dma_addr[7:0]   <= din[7:0];
 
                end
 
        end
 
 
 
 
 
 
 
// fifo,dma,sd control FSMs/etc.
 
        
 
        reg init;
 
        wire wr_stb,rd_stb;
 
        wire wdone,rdone,empty;
 
 
 
        wire [7:0] fifo_wd; // data for FIFO to be written
 
        wire [7:0] fido_rd; // data read from FIFO
 
 
 
        // MUX data to FIFO
 
        assign fifo_wd       = dma_snr ? dma_rd  : sd_receiveddata;
 
        // MUX data to SDcard
 
        assign sd_datatosend = dma_snr ? fifo_rd : 8'hFF;
 
        
 
        // connect dma in to fifo out without muxing
 
        assign dma_wd = fifo_rd;
 
 
 
        fifo512_oneshot fifo512_oneshot ( .clk(clk),
 
                                          .rst_n(rst_n),
 
 
 
                                          .init(init),
 
 
 
                                          .wr_stb(wr_stb),
 
                                          .rd_stb(rd_stb),
 
 
 
                                          .wdone(wdone),
 
                                          .rdone(rdone),
 
                                          .empty(empty),
 
 
 
                                          .wd(fifo_wd),
 
                                          .rd(fifo_rd)
 
                                        );
 
        // fifo control
 
        reg sd_wr_stb,sd_rd_stb;   // set in SD FSM
 
        reg dma_wr_stb,dma_rd_stb; // set in DMA FSM
 
        //      
 
        assign wr_stb = sd_wr_stb | dma_wr_stb;
 
        assign rd_stb = sd_rd_stb | dma_rd_stb;
 
 
 
        // dma control
 
        wire dma_put_req;
 
        wire dma_get_req;
 
        
 
        assign dma_req = dma_put_req | dma_get_req;
 
 
 
 
 
 
 
        reg sd_go; // start strobe for SD FSM
 
        reg sd_idle; // whether SD FSM is idle
 
 
 
        reg dma_go;
 
        reg dma_idle;
 
 
 
 
 
 
 
 
 
 
 
 
 
        // SD-card controlling FSM
 
        
 
        reg [2:0] sd_state, sd_next_state;
 
        
 
        localparam SD_IDLE   = 3'b000;
 
        localparam SD_READ1  = 3'b100;
 
        localparam SD_READ2  = 3'b101;
 
        localparam SD_WRITE1 = 3'b110;
 
        localparam SD_WRITE2 = 3'b111;
 
        
 
        always @(posedge clk, negedge rst_n)
 
        begin
 
                if( !rst_n )
 
                begin
 
                        sd_state = SD_IDLE;
 
                end
 
                else // posedge clk
 
                begin
 
                        sd_state <= sd_next_state;
 
                end
 
        end
 
 
 
        always @*
 
        begin
 
                case( sd_state )
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
                SD_IDLE:begin
 
                        if( sd_go )
 
                        begin
 
                                if( sd_snr ) // send to SD
 
                                begin
 
                                        sd_next_state = SD_WRITE1;
 
                                end
 
                                else // !sd_snr: read from SD
 
                                begin
 
                                        sd_next_state = SD_READ1;
 
                                end
 
                        end
 
                        else
 
                        begin
 
                                sd_next_state = SD_IDLE;
 
                        end
 
                end
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
                SD_READ1:begin
 
                        if( wdone )
 
                        begin
 
                                sd_next_state = SD_IDLE;
 
                        end
 
                        else // !wdone - can still send bytes to the fifo
 
                        begin
 
                                if( !sd_rdy ) // not ready with previous byte - wait here
 
                                begin
 
                                        sd_next_state = SD_READ1;
 
                                end
 
                                else // sd_rdy - can proceed further
 
                                begin
 
                                        sd_next_state = SD_READ2;
 
                                end
 
                        end
 
                end
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
                SD_READ2:begin
 
                        sd_next_state = SD_READ1;
 
                end
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
                SD_WRITE1:begin
 
                        if( rdone )
 
                        begin
 
                                sd_next_state = SD_IDLE;
 
                        end
 
                        else
 
                        begin
 
                                if( sd_rdy && !empty ) // whether sd ready and we can take next byte from fifo
 
                                begin
 
                                        sd_next_state = SD_WRITE2;
 
                                end
 
                                else // can't start next byte: wait
 
                                begin
 
                                        sd_next_state = SD_WRITE1;
 
                                end
 
                        end
 
                end
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
                SD_WRITE2:begin
 
                        sd_next_state = SD_WRITE1;
 
                end
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
                default:begin
 
                        sd_next_state = SD_IDLE;
 
                end
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
                endcase
 
        end
 
 
 
        always @(posedge clk, negedge rst_n)
 
        begin
 
                if( !rst_n )
 
                begin
 
                        sd_wr_stb   = 1'b0;
 
                        sd_rd_stb   = 1'b0;
 
                        sd_start    = 1'b0;
 
                        sd_override = 1'b0;
 
                
 
                        sd_idle     = 1'b0;
 
                end
 
                else // posedge clk
 
                begin
 
                        case( sd_next_state )
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
                        SD_IDLE:begin
 
                                sd_wr_stb   <= 1'b0;
 
                                sd_rd_stb   <= 1'b0;
 
                                sd_start    <= 1'b0;
 
                                sd_override <= 1'b0;
 
                                
 
                                sd_idle     <= 1'b1;
 
                        end
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
                        SD_READ1:begin
 
                                sd_override <= 1'b1; // takeover SD card SPI iface
 
                                sd_start    <= 1'b0;
 
                                sd_wr_stb   <= 1'b0;
 
                                
 
                                sd_idle     <= 1'b0;
 
                        end
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
                        SD_READ2:begin
 
                                sd_start  <= 1'b1; // trigger new SPI exchange
 
                                sd_wr_stb <= 1'b1; // trigger FIFO write
 
                        end
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
                        SD_WRITE1:begin
 
                                sd_override <= 1'b1;
 
                                sd_start    <= 1'b0;
 
                                sd_rd_str   <= 1'b0;
 
                                
 
                                sd_idle     <= 1'b0;
 
                        end
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
                        SD_WRITE2:begin
 
                                sd_start  <= 1'b1;
 
                                sd_rd_stb <= 1'b1;
 
                        end
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
                        endcase
 
                end
 
        end
 
        
 
 
 
 
 
 
 
 
 
 
 
        // DMA-controlling FSM
 
 
 
        reg [3:0] dma_state, dma_next_state
 
        
 
        localparam DMA_IDLE
 
        localparam DMA_PUT_WAIT
 
        localparam DMA_PUT_RUN
 
 
 
        always @(posedge clk, negedge rst_n)
 
        begin
 
                if( !rst_n )
 
                begin
 
                        dma_state = DMA_IDLE;
 
                end
 
                else // posedge clk
 
                begin
 
                        dma_state <= dma_next_state;
 
                end
 
        end
 
 
 
        always @*
 
        begin
 
                case( dma_state )
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
                DMA_IDLE:begin
 
                        if( dma_go )
 
                        begin
 
                                if( dma_snr )
 
                                begin
 
                                        ........dma_state = DMA_GET_WAIT;
 
                                end
 
                                else // !dma_snr
 
                                begin
 
                                        dma_next_state = DMA_PUT_WAIT;
 
                                end
 
                        end
 
                        else
 
                        begin
 
                                dma_next_state = DMA_IDLE;
 
                        end
 
                end
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
                DMA_PUT_WAIT:begin
 
                        if( rdone )
 
                        begin
 
                                dma_next_state = DMA_IDLE;
 
                        end
 
                        else // !rdone
 
                        begin
 
                                if( !empty ) // fifo is not empty
 
                                begin
 
                                        dma_next_state = DMA_PUT_RUN;
 
                                end
 
                                else // fifo empty
 
                                begin
 
                                        dma_next_state = DMA_PUT_WAIT;
 
                                end
 
                        end
 
                end
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
                DMA_PUT_RUN:begin
 
                        if( rdone )
 
                        begin
 
                                dma_next_state = DMA_IDLE;
 
                        end
 
                        else
 
                        begin
 
                                if( empty )
 
                                begin
 
                                        dma_next_state = DMA_PUT_WAIT;
 
                                end
 
                                else // !empty
 
                                begin
 
                                        dma_next_state = DMA_PUT_RUN;
 
                                end
 
                        end
 
                end
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
                endcase
 
        end
 
        
 
        always @(posedge clk, negedge rst_n)
 
        begin
 
                if( !rst_n )
 
                begin
 
                
 
                end
 
                else // posedge clk
 
                begin
 
                        case( dma_next_state )
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
                        endcase
 
                end
 
        end
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
endmodule
 
 
 
 
 
 
 
 
 
// this is "one-shot" fifo: after each 512 bytes both written and read back, it must be initialized by means of 'init'
 
//
 
module fifo512_oneshot(
 
 
 
        input  wire clk,
 
        input  wire rst_n,
 
        
 
        input  wire init, // initializes fifo: wptr=rptr=0
 
        
 
        input  wire wr_stb, // write strobe: writes data from wd to the current wptr, increments wptr
 
        input  wire rd_stb, // read strobe: increments rptr
 
        
 
        output wire wdone, // write done - all 512 bytes are written (end of write operation)
 
        output wire rdone, // read done - all 512 bytes are read (end of read operation)
 
        output wire empty, // fifo empty: when wptr==rptr (rd_stb must not be issued when empty is active, otherwise everytrhing desyncs)
 
        
 
        input  wire [7:0] wd, // data to be written
 
        output wire [7:0] rd  // data just read from rptr address
 
);
 
 
 
        reg [9:0] wptr;
 
        reg [9:0] rptr;
 
 
 
        always @(posedge clk, negedge rst_n)
 
        begin
 
                if( !rst_n )
 
                begin
 
                        wptr = 10'd0;
 
                        rptr = 10'd0;
 
                end
 
                else
 
                begin // posedge clk
 
                
 
                        if( init )
 
                        begin
 
                                wptr <= 10'd0;
 
                        end
 
                        else if( wr_stb )
 
                        begin
 
                                wptr <= wptr + 10'd1;
 
                        end
 
                
 
                
 
                        if( init )
 
                        begin
 
                                rptr <= 10'd0;
 
                        end
 
                        else if( rd_stb )
 
                        begin
 
                                rptr <= rptr + 10'd1;
 
                        end
 
                
 
                end
 
        end
 
 
 
        assign wdone = wptr[9];
 
        assign rdone = rptr[9];
 
        assign empty = ( wptr==rptr );
 
 
 
 
 
 
 
        mem512b fifo512_oneshot_mem512b( .clk(clk),
 
 
 
                                         .rdaddr(rptr[8:0]),
 
                                         .dataout(rd),
 
 
 
                                         .wraddr(wptr[8:0]),
 
                                         .datain(wd),
 
                                         .we(wr_stb)
 
                                       );
 
endmodule