// dma_data_consumer
 
// dma_data_supplier
 
// dma_access_controller
 
//
 
// чрфрўр. ўшЄрЄ№ ш яшёрЄ№ Ёрчэ√х юсырёЄш ярь Єш, юфэютЁхьхээю т 4 яюЄюър
 
// ш ёЁртэштрЄ№ яЁюўшЄрээюх ё чряшёрээ√ь. ╫шЄрЄ№ сєЁёЄрьш 1-N1 срщЄ ё ярєчющ 0-N2 ЄръЄют ьхцфє ърцф√ьш
 
//
 
//
 
 
 
 
 
 
 
module dma_tester(
 
 
 
        input  wire        clk,
 
 
 
        input  wire        can_go,
 
 
 
 
 
        output reg         req,
 
        output reg         rnw,
 
        output reg   [7:0] wd,
 
        output reg  [20:0] addr,
 
 
 
        input  wire        ack,
 
        input  wire        done,
 
        input  wire  [7:0] rd
 
);
 
        parameter BEG_ADDR = 21'h00C000;
 
        parameter BLK_SIZE = 21'd004096;
 
 
 
 
 
 
 
        reg         first; // 1 when first cycle in sequence of either reads or writes
 
 
 
 
 
        reg rnw_done;   // to store rnw until reader gets it at done signal
 
        reg first_done; //
 
 
 
 
 
 
 
        // address/dir generation
 
        //
 
        always @(posedge clk, negedge can_go)
 
        begin
 
                if( !can_go )
 
                begin
 
                        addr  = BEG_ADDR;
 
                        rnw   = 1'b1;     // start with reading
 
                        first = 1'b0;
 
                end
 
                else // posedge clk
 
                begin
 
                        if( ack )
 
                        begin
 
                                if( addr >= (BEG_ADDR+BLK_SIZE-1) )
 
                                begin
 
                                        addr  <= BEG_ADDR;
 
                                        rnw   <= ~rnw;
 
                                        first <= 1'b1;
 
                                end
 
                                else
 
                                begin
 
                                        addr  <= addr + 21'd1;
 
                                        first <= 1'b0;
 
                                end
 
                        end
 
                end
 
        end
 
 
 
 
 
 
 
 
 
 
 
`define RND_PAUSE (8'd20 + ( 8'd63 & ($random>>20)))
 
`define RND_BURST (8'd1 + ( 8'd15 & ($random>>20)))
 
        // burst generator
 
        //
 
        reg [7:0] bcnt;
 
        //
 
        always @(posedge clk, negedge can_go)
 
        begin
 
                if( !can_go )
 
                begin
 
                        bcnt = `RND_PAUSE;
 
                        req  = 1'b0;
 
                end
 
                else // posedge clk
 
                begin
 
 
 
                        if( !req ) // waiting
 
                        begin
 
                                if( bcnt!=0 )
 
                                begin
 
                                        bcnt <= bcnt - 8'd1;
 
                                end
 
                                else // bcnt==0
 
                                begin
 
                                        req  <= 1'b1;
 
                                        bcnt <= `RND_BURST;
 
                                end
 
                        end
 
                        else // req - bursting
 
                        begin
 
                                if( ack )
 
                                begin
 
                                        if( bcnt!=0 )
 
                                        begin
 
                                                bcnt <= bcnt - 8'd1;
 
                                        end
 
                                        else // bcnt==0
 
                                        begin
 
                                                req  <= 1'b0;
 
                                                bcnt <= `RND_PAUSE;
 
                                        end
 
                                end
 
                        end
 
                end
 
        end
 
 
 
 
 
 
 
        // rnd data supplying/checking
 
        //
 
        reg [7:0] chkmem [0:BLK_SIZE-1];
 
        integer wr_ptr,rd_ptr;
 
        integer rnd;
 
        reg idle;
 
        //
 
        initial
 
        begin
 
                idle=1'b1;
 
                wr_ptr = 0;
 
                rd_ptr = 0;
 
        end
 
        //
 
        always @(posedge clk, negedge can_go)
 
        begin
 
                if( !can_go )
 
                begin
 
                        rnw_done   = 1'b1;
 
                        first_done = 1'b0;
 
                end
 
                else // posedge clk
 
                begin
 
                        if( ack )
 
                        begin
 
                                rnw_done   <= rnw;
 
                                first_done <= first;
 
                        end
 
                end
 
        end
 
        //
 
        // idle control
 
        always @(posedge first)
 
        begin
 
                if( idle )
 
                begin
 
                        idle <= 1'b0;
 
                end
 
        end
 
        // reading
 
        always @(posedge clk)
 
        begin
 
                if( !idle )
 
                begin
 
                        if( rnw_done ) // read
 
                        begin
 
                                if( done )
 
                                begin
 
                                        if( first_done ) 
 
                                        begin
 
                                                rd_ptr = 0;
 
                                                //$display("reader done!\n");
 
                                        end
 
 
 
                                        if( chkmem[rd_ptr]!=rd )
 
                                        begin
 
                                                $display("readback error: at address %x must be %x, was %x\n",rd_ptr,chkmem[rd_ptr],rd);
 
                                                $stop;
 
                                        end
 
 
 
                                        rd_ptr=rd_ptr+1;
 
                                end
 
                        end
 
                end
 
        end
 
        //
 
        // writing
 
        always @(posedge clk)
 
        begin
 
                if( idle )
 
                        wd <= $random>>24;
 
                else // !idle
 
                        if( !rnw && ack )
 
                                wd <= $random>>24;
 
        end
 
        //      
 
        always @(posedge clk)
 
        begin
 
                if( !idle )
 
                begin
 
                        if( !rnw ) // write
 
                        begin
 
                                if( ack )
 
                                begin
 
                                        chkmem[wr_ptr] <= wd;
 
                                        
 
                                        if( first )
 
                                                wr_ptr <= 1;
 
                                        else if( wr_ptr<(BLK_SIZE-1) )
 
                                                wr_ptr <= wr_ptr+1;
 
                                        else
 
                                                wr_ptr <= 0;
 
                                end
 
                        end
 
                end
 
        end
 
 
 
 
 
 
 
endmodule