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
//
// ZX dma controller
//
// includes dma address regs, dma control reg
//
// CURRENTLY ONLY READ ON ZXBUS SIDE FROM NGS MEM, error in WAIT generation! zx_dma2.v - further development

module dma_zx(

        input clk,
        input rst_n,


        // ZXBUS-related signals

        input zxdmaread,  // async strobes made directly from zxbus signals
        input zxdmawrite, //

        input      [7:0] dma_wr_data, // data written by ZXBUS here
        output reg [7:0] dma_rd_data, // to be output to the ZXBUS from here

        output reg wait_ena, // for zxbus module, to stop temporarily ZX Z80




        // different global & control signals

        output reg dma_on,


        // signals from ports.v

        input      [7:0] din,  // input and output from ports.v
        output reg [7:0] dout,

        input module_select, // =1 - module selected for read-write operations from ports.v
        input write_strobe, // one-cycle positive write strobe - writes to the selected registers from din

        input [1:0] regsel, // 2'b00 - high address, 2'b01 - middle address, 2'b10 - low address, 2'b11 - control register


        // signals for DMA controller

      output reg [20:0] dma_addr,
      output reg  [7:0] dma_wd,
      input       [7:0] dma_rd,
      output reg        dma_rnw,

      output reg dma_req,
      input      dma_ack,
      input      dma_end

);

        reg [7:0] dma_rd_temp; // temporarily buffered read data from DMA module

        reg [2:0] zxdmaread_sync;  // syncing appropriate zxbus signals
        reg [2:0] zxdmawrite_sync;

        reg zxread_begin, zxwrite_begin; // 1-cycle positive pulses based on synced in zxdmaread and zxdmawrite
        reg zxread_end,   zxwrite_end;   //


        reg dma_prereq; // to help assert dma_req one cycle earlier



        reg [1:0] waena_state,waena_next; // and wait_ena generation

        reg [1:0] dmarq_state,dmarq_next; // DMA req gen



        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

        // 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, 7'bXXXXXXX };
        endcase

        // ports.v write access & dma_addr control
        always @(posedge clk, negedge rst_n)
        if( !rst_n ) // async reset
        begin
                dma_on <= 1'b0;
        end
        else // posedge clk
        begin
                // dma_on control
                if( module_select && write_strobe && (regsel==_CST) )
                        dma_on <= din[7];

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



        // syncing in zxdmaread and zxdmawrite, making _begin and _end pulses
        always @(posedge clk)
        begin
                zxdmaread_sync[2:0]  <= { zxdmaread_sync[1:0],  zxdmaread  };
                zxdmawrite_sync[2:0] <= { zxdmawrite_sync[1:0], zxdmawrite };
        end

        always @*
        begin
                zxread_begin  <= zxdmaread_sync[1]  && (!zxdmaread_sync[2]);
                zxwrite_begin <= zxdmawrite_sync[1] && (!zxdmawrite_sync[2]);

                zxread_end  <= (!zxdmaread_sync[1])  && zxdmaread_sync[2];
                zxwrite_end <= (!zxdmawrite_sync[1]) && zxdmawrite_sync[2];
        end



        // temporary: dma_rnw always at read state
        always @* dma_rnw = 1'b1;



        // FSM for wait_enable

        localparam waenaIDLE = 0;
        localparam waenaWAIT = 1;

        always @(posedge clk, negedge rst_n)
        if( !rst_n )
                waena_state <= waenaIDLE;
        else if( !dma_on )
                waena_state <= waenaIDLE;
        else
                waena_state <= waena_next;

        always @*
        case( waena_state )
                waenaIDLE:
                        if( zxread_end && (!dma_end) )
                                waena_next <= waenaWAIT;
                        else
                                waena_next <= waenaIDLE;
                waenaWAIT:
                        if( dma_end )
                                waena_next <= waenaIDLE;
                        else
                                waena_next <= waenaWAIT;
        endcase

        always @(posedge clk, negedge rst_n)
        if( !rst_n )
                wait_ena <= 1'b0;
        else if( !dma_on )
                wait_ena <= 1'b0;
        else
        case( waena_next )
                waenaIDLE:
                        wait_ena <= 1'b0;
                waenaWAIT:
                        wait_ena <= 1'b1;
        endcase



        // FSM for dma request

        localparam dmarqIDLE = 0;
        localparam dmarqREQ1 = 1;
        localparam dmarqREQ2 = 2;

        always @(posedge clk, negedge rst_n)
        if( !rst_n )
                dmarq_state <= dmarqIDLE;
        else if( !dma_on )
                dmarq_state <= dmarqIDLE;
        else
                dmarq_state <= dmarq_next;

        always @*
        case( dmarq_state )
                dmarqIDLE:
                        if( zxread_begin )
                                dmarq_next <= dmarqREQ1;
                        else
                                dmarq_next <= dmarqIDLE;
                dmarqREQ1:
                        if( dma_ack && (!zxread_begin) )
                                dmarq_next <= dmarqIDLE;
                        else if( (!dma_ack) && zxread_begin )
                                dmarq_next <= dmarqREQ2;
                        else // nothing or both zxread_begin and dma_ack
                                dmarq_next <= dmarqREQ1;
                dmarqREQ2:
                        if( dma_ack )
                                dmarq_next <= dmarqREQ1;
                        else
                                dmarq_next <= dmarqREQ2;
        endcase

        always @(posedge clk, negedge rst_n)
        if( !rst_n )
                dma_prereq <= 1'b0;
        else
        case( dmarq_next )
                dmarqIDLE:
                        dma_prereq <= 1'b0;
                dmarqREQ1:
                        dma_prereq <= 1'b1;
                dmarqREQ2:
                        dma_prereq <= 1'b1;
        endcase

        always @* dma_req <= (dma_prereq | zxread_begin) & dma_on;


        // pick up data from DMA

        always @(posedge clk) if( dma_end ) dma_rd_temp <= dma_rd;

        always @(posedge clk)
        begin
                if( zxread_end && dma_end ) // simultaneously coming zxread_end and dma_end: get data directly from dma
                        dma_rd_data <= dma_rd;
                else if( dma_end && wait_ena ) // dma_end was after zxread_end: get data directly from dma
                        dma_rd_data <= dma_rd;
                else if( zxread_end )
                        dma_rd_data <= dma_rd_temp; // we can always corrupt dma_rd_data at zxread_end strobe, even if we
                                                    // will overwrite it with newer arrived data later
        end


endmodule