Top secrets sources NedoPC ngs

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed | ?url?

// 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