// part of NeoGS project
 
//
 
// (c) NedoPC 2007-2008
 
//
 
// modelling is in tb_dma1.*
 
// look also at dma_access.png
 
 
 
module dma_access(
 
 
 
        input            clk,
 
 
 
        input            rst_n,
 
 
 
 
 
        input            dma_req,  // DMA request
 
        input     [20:0] dma_addr, // DMA address (2mb)
 
        input            dma_rnw,  // DMA READ/nWRITE
 
        input      [7:0] dma_wd,   // DMA data to write
 
        output reg [7:0] dma_rd,   // DMA data just read
 
 
 
        output reg       dma_busynready, // DMA BUSY/nREADY
 
        output reg       dma_ack, // positive pulse as dma_busynready goes high
 
        output reg       dma_end, // positive pulse as dma_busynready goes low
 
 
 
        output wire        mem_dma_bus,  // DMA taking over the bus
 
        output wire [20:0] mem_dma_addr, // DMA address going to the bus
 
        output wire  [7:0] mem_dma_wd,   // DMA data going to the bus
 
        input        [7:0] mem_dma_rd,   // DMA data going from the bus
 
        output wire        mem_dma_rnw,  // DMA bus direction (1=read, 0=write)
 
        output reg         mem_dma_oe,   // DMA read strobe going to the bus
 
        output reg         mem_dma_we,   // DMA write pulse going to the bus
 
 
 
 
 
        output reg       busrq_n, // CPU       signals
 
        input            busak_n  //    control
 
);
 
 
 
        reg dma_bus;
 
 
 
        reg [20:0] int_dma_addr;
 
        reg        int_dma_rnw;
 
        reg  [7:0] int_dma_wd;
 
        wire [7:0] int_dma_rd;
 
 
 
        assign mem_dma_bus  = dma_bus;
 
        assign mem_dma_addr = int_dma_addr;
 
        assign mem_dma_wd   = int_dma_wd;
 
        assign mem_dma_rnw  = int_dma_rnw;
 
        assign int_dma_rd   = mem_dma_rd;
 
 
 
 
 
 
 
        localparam IDLE     = 0;
 
        localparam START    = 1;
 
        localparam WACK     = 2;
 
        localparam READ1    = 3;
 
        localparam READ2    = 4;
 
        localparam WRITE1   = 5;
 
        localparam WRITE2   = 6;
 
 
 
 
 
        reg [3:0] state;
 
        reg [3:0] next_state;
 
 
 
 
 
 
 
 
 
        // for simulation purposes
 
        initial
 
        begin
 
                state       <= IDLE;
 
                busrq_n     <= 1'b1;
 
                mem_dma_oe  <= 1'b1;
 
                mem_dma_we  <= 1'b1;
 
        end
 
 
 
 
 
// FSM
 
        always @(posedge clk, negedge rst_n)
 
        begin
 
                if( !rst_n )
 
                        state <= IDLE;
 
                else
 
                        state <= next_state;
 
        end
 
 
 
 
 
        always @*
 
        begin
 
                case( state )
 
//////////////////////////////////////////////////////////////////////////////////////////
 
                IDLE:
 
                begin
 
                        if( dma_req==1'b1 )
 
                                next_state <= START;
 
                        else
 
                                next_state <= IDLE;
 
                end
 
//////////////////////////////////////////////////////////////////////////////////////////
 
                START:
 
                begin
 
                        next_state <= WACK;
 
                end
 
//////////////////////////////////////////////////////////////////////////////////////////
 
                WACK:
 
                begin
 
                        if( busak_n == 1'b1 ) ///// ACHTUNG WARNING!!! probably use here registered busak?
 
                                next_state <= WACK;
 
                        else // busak_n == 1'b0
 
                        begin
 
                                if( int_dma_rnw == 1'b1 ) // read
 
                                        next_state <= READ1;
 
                                else // int_dma_rnw == 1'b0 - write
 
                                        next_state <= WRITE1;
 
                        end
 
                end
 
//////////////////////////////////////////////////////////////////////////////////////////
 
                READ1:
 
                begin
 
                        next_state <= READ2;
 
                end
 
//////////////////////////////////////////////////////////////////////////////////////////
 
                READ2:
 
                begin
 
                        if( dma_req == 1'b0 )
 
                                next_state <= IDLE;
 
                        else // dma_req == 1'b1
 
                        begin
 
                                if( dma_rnw == 1'b1 ) // next is read
 
                                        next_state <= READ1;
 
                                else // dma_rnw == 1'b0 - next is write
 
                                        next_state <= WRITE1;
 
                        end
 
                end
 
//////////////////////////////////////////////////////////////////////////////////////////
 
                WRITE1:
 
                begin
 
                        next_state <= WRITE2;
 
                end
 
//////////////////////////////////////////////////////////////////////////////////////////
 
                WRITE2:
 
                begin
 
                        if( dma_req == 1'b0 )
 
                                next_state <= IDLE;
 
                        else // dma_req == 1'b1
 
                        begin
 
                                if( dma_rnw == 1'b1 ) // next is read
 
                                        next_state <= READ1;
 
                                else // dma_rnw == 1'b0 - next is write
 
                                        next_state <= WRITE1;
 
                        end
 
                end
 
//////////////////////////////////////////////////////////////////////////////////////////
 
                endcase
 
        end
 
 
 
 
 
        always @(posedge clk, negedge rst_n)
 
        begin
 
                if( !rst_n )
 
                begin
 
                        busrq_n        <= 1'b1;
 
                        dma_busynready <= 1'b0;
 
                        dma_ack        <= 1'b0;
 
                        dma_end        <= 1'b0;
 
                        dma_bus        <= 1'b0;
 
                        mem_dma_oe     <= 1'b1;
 
                end
 
                else case( next_state )
 
//////////////////////////////////////////////////////////////////////////////////////////
 
                IDLE:
 
                begin
 
                        dma_end        <= 1'b0;
 
 
 
                        busrq_n        <= 1'b1;
 
                        dma_bus        <= 1'b0;
 
                        mem_dma_oe     <= 1'b1;
 
                end
 
//////////////////////////////////////////////////////////////////////////////////////////
 
                START:
 
                begin
 
//                      dma_bus        <= 1'b0; // if rst=0>1 and dma_ack=1 --> ??? is this really needed?
 
 
 
 
 
                        busrq_n        <= 1'b0;
 
 
 
                        dma_busynready <= 1'b1;
 
                        dma_ack        <= 1'b1;
 
 
 
                        int_dma_rnw    <= dma_rnw;
 
                        int_dma_addr   <= dma_addr;
 
                        int_dma_wd     <= dma_wd;
 
                end
 
//////////////////////////////////////////////////////////////////////////////////////////
 
                WACK:
 
                begin
 
                        dma_ack <= 1'b0;
 
                end
 
//////////////////////////////////////////////////////////////////////////////////////////
 
                READ1:
 
                begin
 
                        dma_bus    <= 1'b1; // take over the bus
 
                        mem_dma_oe <= 1'b0;
 
                        if( dma_busynready == 1'b0 ) // if we are here from READ2 or WRITE2
 
                        begin
 
                                dma_busynready <= 1'b1;
 
                                dma_ack        <= 1'b1;
 
                                dma_end        <= 1'b0;
 
                                int_dma_rnw    <= 1'b1;
 
                                int_dma_addr   <= dma_addr;
 
                        end
 
                end
 
//////////////////////////////////////////////////////////////////////////////////////////
 
                READ2:
 
                begin
 
                        dma_busynready <= 1'b0;
 
                        dma_ack        <= 1'b0;
 
                        dma_end        <= 1'b1;
 
                        dma_rd <= int_dma_rd;
 
                end
 
//////////////////////////////////////////////////////////////////////////////////////////
 
                WRITE1:
 
                begin
 
                        dma_bus    <= 1'b1; // take over the bus
 
                        mem_dma_oe <= 1'b1;
 
 
 
                        if( dma_busynready == 1'b0 ) // from READ2 or WRITE2
 
                        begin
 
                                dma_busynready <= 1'b1;
 
                                dma_ack        <= 1'b1;
 
                                dma_end        <= 1'b0;
 
                                int_dma_rnw    <= 1'b0;
 
                                int_dma_addr   <= dma_addr;
 
                                int_dma_wd     <= dma_wd;
 
                        end
 
                end
 
//////////////////////////////////////////////////////////////////////////////////////////
 
                WRITE2:
 
                begin
 
                        dma_busynready <= 1'b0;
 
                        dma_ack        <= 1'b0;
 
                        dma_end        <= 1'b1;
 
                end
 
//////////////////////////////////////////////////////////////////////////////////////////
 
                endcase
 
        end
 
 
 
 
 
 
 
 
 
// mem_dma_we generator
 
 
 
        always @(negedge clk,negedge rst_n)
 
        begin
 
                if( !rst_n )
 
                        mem_dma_we <= 1'b1;
 
                else
 
                begin
 
                        if( dma_bus )
 
                        begin
 
                                if( !int_dma_rnw )
 
                                        mem_dma_we <= ~mem_dma_we;
 
                        end
 
                        else
 
                                mem_dma_we <= 1'b1;
 
                end
 
        end
 
 
 
 
 
endmodule