module darkcache
(
    input           CLK,    // clock
    input           RES,    // reset

    input           RW,     // read/write
    input   [2:0]   DLEN,   // data length in bytes

    input   [31:0]  ADDR,   // address
    input   [31:0]  DATA,   // data input
    output  [31:0]  DATO,   // data output
    
    output          HIT,    // cache hit
    output          DTREQ,  // cache miss, data transfer req
    input           DTACK,  // cache miss, data transfer ack
    
    output  [3:0]   DEBUG   // osciloscope
);

    reg  [31:0] CDATA [0:2**`DEPTH-1];
    reg  [31:`DEPTH+2] CTAG  [0:2**`DEPTH-1];
    
    // reg  [2**`DEPTH-1:0] CVALID = 0;
    reg  CVALID [0:2**`DEPTH-1];
    
    reg  [31:0] CDATAFF;
    
    integer i;
    
    initial for(i=0;i!=2**`DEPTH;i=i+1) CVALID[i] = 0;

    wire [`DEPTH-1:0]  CINDEX = ADDR[`DEPTH+1:2];

    assign HIT = RES ? 0 : 
                 (DLEN && RW==1 && CVALID[CINDEX] && CTAG[CINDEX]==ADDR[31:`DEPTH+2])||
                 (DLEN && RW==0 && DTREQ && DTACK);

    assign DTREQ = RES ? 0 : DLEN && (!HIT||!RW); // valid data, but not hit -> dtreq

    // debug only

    wire [7:0] DATX [0:3];
    
    assign DATX[0] = DATO[ 7: 0];
    assign DATX[1] = DATO[15: 8];
    assign DATX[2] = DATO[23:16];
    assign DATX[3] = DATO[31:24];

    always@(posedge CLK)
    begin
        if(DTREQ&&DTACK)
        begin
            CDATA [CINDEX]  <= DATA;
            CTAG  [CINDEX]  <= ADDR[31:`DEPTH+2];
            CVALID[CINDEX]  <= RW;
        end
        
        if(!RES) $display("cache %s ADDR=%x RW=%d DTREQ=%d DTACK=%d DATA=%x DATO=%x BUS=%s",
                                (HIT?"hit":"miss"),
                                ADDR,RW,DTREQ,DTACK,DATA,DATX[ADDR[1:0]],
                                (DTREQ?"busy":"idle")); 

        CDATAFF <= CDATA[CINDEX];
    end

    assign DATO  = CDATAFF;

    assign DEBUG = { |DLEN, HIT, DTREQ, DTACK };

endmodule