data/full_repos/permissive/137045072/data_cache.v
 line:414: before: "="
1: b'%Error: data/full_repos/permissive/137045072/data_cache.v:27: Cannot find include file: defines.v\n`include "defines.v" \n         ^~~~~~~~~~~\n        ... Looked in:\n             data/full_repos/permissive/137045072,data/full_repos/permissive/137045072/defines.v\n             data/full_repos/permissive/137045072,data/full_repos/permissive/137045072/defines.v.v\n             data/full_repos/permissive/137045072,data/full_repos/permissive/137045072/defines.v.sv\n             defines.v\n             defines.v.v\n             defines.v.sv\n             obj_dir/defines.v\n             obj_dir/defines.v.v\n             obj_dir/defines.v.sv\n%Error: data/full_repos/permissive/137045072/data_cache.v:42: Define or directive not defined: \'`DATA_BUS\'\n    input [`DATA_BUS] addr,\n           ^~~~~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:42: syntax error, unexpected \']\', expecting TYPE-IDENTIFIER\n    input [`DATA_BUS] addr,\n                    ^\n%Error: data/full_repos/permissive/137045072/data_cache.v:46: Define or directive not defined: \'`DATA_BUS\'\n    input [`DATA_BUS] write,\n           ^~~~~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:49: Define or directive not defined: \'`DATA_BUS\'\n    output reg [`DATA_BUS] read,\n                ^~~~~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:52: syntax error, unexpected output, expecting IDENTIFIER or \'=\' or do or final\n    output ready,\n    ^~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:55: Define or directive not defined: \'`DATA_BUS\'\n    output reg [`DATA_BUS] mem_addr,\n                ^~~~~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:57: syntax error, unexpected output, expecting IDENTIFIER or \'=\' or do or final\n    output reg mem_rw,\n    ^~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:59: syntax error, unexpected input, expecting IDENTIFIER or \'=\' or do or final\n    input [`DATA_BUS] mem_read,\n    ^~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:59: Define or directive not defined: \'`DATA_BUS\'\n    input [`DATA_BUS] mem_read,\n           ^~~~~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:62: Define or directive not defined: \'`DATA_BUS\'\n    output reg [`DATA_BUS] mem_write,\n                ^~~~~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:107: Define or directive not defined: \'`DATA_BUS\'\n    reg [`DATA_BUS] blocks[0:INDEX_SIZE-1][0:ASSOCIATIVITY-1][0:BLOCK_SIZE-1];  \n         ^~~~~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:107: syntax error, unexpected \']\', expecting TYPE-IDENTIFIER\n    reg [`DATA_BUS] blocks[0:INDEX_SIZE-1][0:ASSOCIATIVITY-1][0:BLOCK_SIZE-1];  \n                  ^\n%Error: data/full_repos/permissive/137045072/data_cache.v:111: syntax error, unexpected assign\n    assign ready = state == STATE_READY;\n    ^~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:114: Define or directive not defined: \'`MEM_READ\'\n    wire cache_read_hit = cache_hit && rw == `MEM_READ && state == STATE_READY;\n                                             ^~~~~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:114: syntax error, unexpected &&, expecting TYPE-IDENTIFIER\n    wire cache_read_hit = cache_hit && rw == `MEM_READ && state == STATE_READY;\n                                                       ^~\n%Error: data/full_repos/permissive/137045072/data_cache.v:115: Define or directive not defined: \'`MEM_WRITE\'\n    wire cache_write_hit = cache_hit && rw == `MEM_WRITE && state == STATE_READY;\n                                              ^~~~~~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:115: syntax error, unexpected &&, expecting TYPE-IDENTIFIER\n    wire cache_write_hit = cache_hit && rw == `MEM_WRITE && state == STATE_READY;\n                                                         ^~\n%Error: data/full_repos/permissive/137045072/data_cache.v:118: Define or directive not defined: \'`MEM_WRITE\'\n    wire populate = rw_reg == `MEM_WRITE && mem_read_valid && cnt == block_offset_reg && state == STATE_POPULATE;\n                              ^~~~~~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:118: syntax error, unexpected &&, expecting TYPE-IDENTIFIER\n    wire populate = rw_reg == `MEM_WRITE && mem_read_valid && cnt == block_offset_reg && state == STATE_POPULATE;\n                                         ^~\n%Error: data/full_repos/permissive/137045072/data_cache.v:119: Define or directive not defined: \'`DATA_BUS\'\n    wire [`DATA_BUS] populate_data = populate ? write_reg : mem_read;\n          ^~~~~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:119: syntax error, unexpected \']\', expecting TYPE-IDENTIFIER\n    wire [`DATA_BUS] populate_data = populate ? write_reg : mem_read;\n                   ^\n%Error: data/full_repos/permissive/137045072/data_cache.v:125: syntax error, unexpected always\n    always @*\n    ^~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:140: Define or directive not defined: \'`DATA_BUS\'\n        input [`DATA_BUS] addr;\n               ^~~~~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:140: syntax error, unexpected \']\', expecting TYPE-IDENTIFIER\n        input [`DATA_BUS] addr;\n                        ^\n%Error: data/full_repos/permissive/137045072/data_cache.v:146: Define or directive not defined: \'`DATA_BUS\'\n        input [`DATA_BUS] write;\n               ^~~~~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:163: Define or directive not defined: \'`DATA_BUS\'\n        input [`DATA_BUS] addr;\n               ^~~~~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:163: syntax error, unexpected \']\', expecting TYPE-IDENTIFIER\n        input [`DATA_BUS] addr;\n                        ^\n%Error: data/full_repos/permissive/137045072/data_cache.v:164: Define or directive not defined: \'`DATA_BUS\'\n        input [`DATA_BUS] data;\n               ^~~~~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:172: syntax error, unexpected always\n    always @(posedge clk, negedge rst_n)\n    ^~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:177: Define or directive not defined: \'`FALSE\'\n            mem_enable <= `FALSE;\n                          ^~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:179: Define or directive not defined: \'`FALSE\'\n            rw_valid <= `FALSE;\n                        ^~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:206: Define or directive not defined: \'`FALSE\'\n            rw_valid <= `FALSE;\n                        ^~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:212: Define or directive not defined: \'`MEM_READ\'\n                        rw_reg <= `MEM_READ;\n                                  ^~~~~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:213: Define or directive not defined: \'`TRUE\'\n                        rw_valid <= `TRUE;\n                                    ^~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:218: Define or directive not defined: \'`MEM_WRITE\'\n                        rw_reg <= `MEM_WRITE;\n                                  ^~~~~~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:219: Define or directive not defined: \'`TRUE\'\n                        rw_valid <= `TRUE;\n                                    ^~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:220: Define or directive not defined: \'`TRUE\'\n                        dirty[block_index][location] <= `TRUE;\n                                                        ^~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:226: Define or directive not defined: \'`TRUE\'\n                        mem_enable <= `TRUE;\n                                      ^~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:244: Define or directive not defined: \'`MEM_READ\'\n                            mem_rw <= `MEM_READ;\n                                      ^~~~~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:256: Define or directive not defined: \'`MEM_WRITE\'\n                            mem_rw <= `MEM_WRITE;\n                                      ^~~~~~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:281: Define or directive not defined: \'`TRUE\'\n                            mem_enable <= `TRUE;\n                                          ^~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:282: Define or directive not defined: \'`MEM_READ\'\n                            mem_rw <= `MEM_READ;\n                                      ^~~~~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:284: Define or directive not defined: \'`FALSE\'\n                            dirty[block_index_reg][location_reg] <= `FALSE;\n                                                                    ^~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:298: Define or directive not defined: \'`MEM_READ\'\n                        if (cnt == block_offset_reg && rw_reg == `MEM_READ)\n                                                                 ^~~~~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:311: Define or directive not defined: \'`TRUE\'\n                            valid[block_index_reg][location_reg] <= `TRUE;\n                                                                    ^~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:312: Define or directive not defined: \'`FALSE\'\n                            mem_enable <= `FALSE;\n                                          ^~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:314: Define or directive not defined: \'`MEM_WRITE\'\n                            if (rw_reg == `MEM_WRITE)\n                                          ^~~~~~~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:316: Define or directive not defined: \'`TRUE\'\n                                dirty[block_index_reg][location_reg] <= `TRUE;\n                                                                        ^~~~~\n%Error: data/full_repos/permissive/137045072/data_cache.v:320: Define or directive not defined: \'`FALSE\'\n                                dirty[block_index_reg][location_reg] <= `FALSE;\n                                                                        ^~~~~~\n%Error: Exiting due to too many errors encountered; --error-limit=50\n'
module data_cache#(
    parameter DATA_WIDTH = 32,
    parameter DATA_PER_BYTE_WIDTH = 2,

    parameter ASSO_WIDTH = 1,
    parameter BLOCK_OFFSET_WIDTH = 5,
    parameter INDEX_WIDTH = 3,
    parameter TAG_WIDTH = DATA_WIDTH - INDEX_WIDTH - BLOCK_OFFSET_WIDTH - DATA_PER_BYTE_WIDTH
)(
    input clk,
    input rst_n,


    input [`DATA_BUS] addr,
    input enable,
    input rw,

    input [`DATA_BUS] write,
    input [2**DATA_PER_BYTE_WIDTH-1:0] mem_sel,

    output reg [`DATA_BUS] read,
    output reg rw_valid = 0,

    output ready,


    output reg [`DATA_BUS] mem_addr,
    output reg mem_enable,
    output reg mem_rw,

    input [`DATA_BUS] mem_read,
    input mem_read_valid,

    output reg [`DATA_BUS] mem_write,
    input mem_write_req_input,

    
    input mem_last
    );


    localparam ASSOCIATIVITY = 1 << ASSO_WIDTH;
    localparam BLOCK_SIZE = 1 << BLOCK_OFFSET_WIDTH;
    localparam INDEX_SIZE = 1 << INDEX_WIDTH;
    localparam TAG_SIZE = 1 << TAG_WIDTH;


    localparam STATE_READY = 0;
    localparam STATE_PAUSE = 1;
    localparam STATE_POPULATE = 2;
    localparam STATE_WRITEOUT = 3;


    integer i, j, k;
    
    reg [1:0] state;
    reg rw_internal;


    wire [TAG_WIDTH         -1:0] tag  = addr[DATA_WIDTH-1:INDEX_WIDTH+BLOCK_OFFSET_WIDTH+DATA_PER_BYTE_WIDTH];
    wire [INDEX_WIDTH       -1:0] block_index  = addr[INDEX_WIDTH+BLOCK_OFFSET_WIDTH+DATA_PER_BYTE_WIDTH-1:BLOCK_OFFSET_WIDTH+DATA_PER_BYTE_WIDTH];
    wire [BLOCK_OFFSET_WIDTH-1:0] block_offset = addr[BLOCK_OFFSET_WIDTH+DATA_PER_BYTE_WIDTH-1:DATA_PER_BYTE_WIDTH];
    reg  [ASSO_WIDTH        -1:0] location;
    

    reg                           rw_reg;
    reg  [DATA_WIDTH        -1:0] addr_reg;
    reg  [TAG_WIDTH         -1:0] tag_reg;
    reg  [INDEX_WIDTH       -1:0] block_index_reg;
    reg  [BLOCK_OFFSET_WIDTH-1:0] block_offset_reg;
    reg  [ASSO_WIDTH        -1:0] location_reg;
    reg  [DATA_WIDTH        -1:0] write_reg;
    reg  [                   3:0] mem_sel_reg;

    reg [BLOCK_OFFSET_WIDTH-1:0] cnt;


    reg [TAG_WIDTH-1:0] tags[0:INDEX_SIZE-1][0:ASSOCIATIVITY-1];
    reg [`DATA_BUS] blocks[0:INDEX_SIZE-1][0:ASSOCIATIVITY-1][0:BLOCK_SIZE-1];
    reg valid[0:INDEX_SIZE-1][0:ASSOCIATIVITY-1];
    reg dirty[0:INDEX_SIZE-1][0:ASSOCIATIVITY-1];
    
    assign ready = state == STATE_READY;

    wire cache_hit = enable && valid[block_index][location] && tags[block_index][location] === tag && !rw_valid;
    wire cache_read_hit = cache_hit && rw == `MEM_READ && state == STATE_READY;
    wire cache_write_hit = cache_hit && rw == `MEM_WRITE && state == STATE_READY;
    wire cache_miss = !cache_hit && enable && !rw_valid;

    wire populate = rw_reg == `MEM_WRITE && mem_read_valid && cnt == block_offset_reg && state == STATE_POPULATE;
    wire [`DATA_BUS] populate_data = populate ? write_reg : mem_read;
        
    localparam LOCATION_X = {ASSO_WIDTH{1'bx}};
    localparam TAG_X = {TAG_WIDTH{1'bx}};


    always @*
    begin
        location = LOCATION_X;
        for (i = 0; i < ASSOCIATIVITY; i = i + 1)
            if (tags[block_index][i] == tag)
                location = i;
        if (location === LOCATION_X)
            for (i = 0; i < ASSOCIATIVITY; i = i + 1)
                if (tags[block_index][i] === TAG_X)
                    location = i;
        if (location === LOCATION_X)
            location = 0;
    end
    
    task write_task;
        input [`DATA_BUS] addr;
        input [INDEX_WIDTH       -1:0] block_index;
        input [BLOCK_OFFSET_WIDTH-1:0] block_offset;
        input [ASSO_WIDTH        -1:0] location;
        input                    [3:0] mem_sel;
    
        input [`DATA_BUS] write;
    
        begin
            if (mem_sel[3])
                blocks[block_index][location][block_offset][31:24] <= write[31:24];
            if (mem_sel[2])
                blocks[block_index][location][block_offset][23:16] <= write[23:16];
            if (mem_sel[1])
                blocks[block_index][location][block_offset][15: 8] <= write[15: 8];
            if (mem_sel[0])
                blocks[block_index][location][block_offset][ 7: 0] <= write[ 7: 0];

            $display("Data cache write %h on addr %h in mode %b", write, addr, mem_sel);
        end
    endtask
    
    task read_task;
        input [`DATA_BUS] addr;
        input [`DATA_BUS] data;
        
        begin
            read <= data;
            $display("Data cache read %h on addr %h", data, addr);
        end
    endtask

    always @(posedge clk, negedge rst_n)
    begin
        if (!rst_n)
        begin
            state <= STATE_READY;
            mem_enable <= `FALSE;
            
            rw_valid <= `FALSE;
            
            block_offset_reg <= 0;
            block_index_reg <= 0;
            tag_reg <= 0;
            mem_sel_reg <= 0;
            location_reg <= 0;
            rw_reg <= 0;
            write_reg <= 0;
            cnt <= 0;
            mem_rw <= 0;
            mem_addr <= 0;
            mem_write <= 0;
            read <= 0;
            
            for (i = 0; i < INDEX_SIZE; i = i + 1)
                for (j = 0; j < ASSOCIATIVITY; j = j + 1)
                begin
                    tags[i][j] <= TAG_X;
                    valid[i][j] <= 0;
                    dirty[i][j] <= 0;
                    for (k = 0; k < BLOCK_SIZE; k = k + 1)
                        blocks[i][j][k] <= 0;
                end
        end
        else
        begin
            rw_valid <= `FALSE;

            case (state)
                STATE_READY: begin
                    if (cache_read_hit)
                    begin
                        rw_reg <= `MEM_READ;
                        rw_valid <= `TRUE;
                        read_task(addr, blocks[block_index][location][block_offset]);
                    end
                    else if (cache_write_hit)
                    begin
                        rw_reg <= `MEM_WRITE;
                        rw_valid <= `TRUE;
                        dirty[block_index][location] <= `TRUE;
                        read <= 0;
                        write_task(addr, block_index, block_offset, location, mem_sel, write);
                    end
                    else if (cache_miss)
                    begin
                        mem_enable <= `TRUE;

                        block_offset_reg <= block_offset;
                        block_index_reg <= block_index;
                        mem_sel_reg <= mem_sel;
                        tag_reg <= tag;
                        location_reg <= location;
                        addr_reg <= addr;
                        rw_reg <= rw;
                        write_reg <= write;




                        if (!valid[block_index][location] || !dirty[block_index][location])
                        begin
                            cnt <= 0;
                            state <= STATE_POPULATE;
                            mem_rw <= `MEM_READ;
                            mem_addr <= {tag, block_index, {BLOCK_OFFSET_WIDTH+DATA_PER_BYTE_WIDTH{1'b0}}};
                            
`ifdef DEBUG_DATA
                            $display("Data cache miss on unchanged block %x", addr);
`endif
                        end

                        else if (dirty[block_index][location])
                        begin
                            cnt <= 0;
                            state <= STATE_WRITEOUT;
                            mem_rw <= `MEM_WRITE;
                            mem_addr <= {tags[block_index][location], block_index, {BLOCK_OFFSET_WIDTH+DATA_PER_BYTE_WIDTH{1'b0}}};
                            
 `ifdef DEBUG_DATA
                            $display("Data cache miss on changed block %x", addr);
 `endif
                        end
                    end
                end

                STATE_WRITEOUT: begin
                    if (mem_write_req_input)
                    begin
                        mem_write <= blocks[block_index_reg][location_reg][cnt + 1];

                        cnt <= cnt + 1;
                    end
                    else
                    begin
                        if (mem_last)
                        begin
                            cnt <= 0;


                            state <= STATE_POPULATE;
                            mem_enable <= `TRUE;
                            mem_rw <= `MEM_READ;
                            mem_addr <= {tag_reg, block_index_reg, {BLOCK_OFFSET_WIDTH{1'b0}}, {DATA_PER_BYTE_WIDTH{1'b0}}};
                            dirty[block_index_reg][location_reg] <= `FALSE;
                            mem_write <= 'bx;
                        end
                        else if (cnt == 0)
                            mem_write <= blocks[block_index_reg][location_reg][cnt];
                    end
                end


                STATE_POPULATE: begin
                    if (mem_read_valid)
                    begin
                        blocks[block_index_reg][location_reg][cnt] <= populate_data;

                        if (cnt == block_offset_reg && rw_reg == `MEM_READ)
                        begin
                            read_task(addr_reg, populate_data);
`ifdef DEBUG_DATA
                            $display("Data cache populated addr %x, data %x", addr_reg, populate_data);
`endif
                        end

                        cnt <= cnt + 1;

                        if (mem_last)
                        begin
                            tags[block_index_reg][location_reg] <= tag_reg;
                            valid[block_index_reg][location_reg] <= `TRUE;
                            mem_enable <= `FALSE;

                            if (rw_reg == `MEM_WRITE)
                            begin
                                dirty[block_index_reg][location_reg] <= `TRUE;
                                write_task(addr_reg, block_index_reg, block_offset_reg, location_reg, mem_sel_reg, write_reg);
                            end
                            else
                                dirty[block_index_reg][location_reg] <= `FALSE;

                            state <= STATE_PAUSE;
                            block_offset_reg <= BLOCK_SIZE;
                        end
                    end
                end


                STATE_PAUSE: begin
                    rw_valid <= `TRUE;
                    state <= STATE_READY;
                end
            endcase
        end
    end
    
endmodule
module data_cache#(
    parameter DATA_WIDTH = 32,
    parameter DATA_PER_BYTE_WIDTH = 2,

    parameter ASSO_WIDTH = 1,
    parameter BLOCK_OFFSET_WIDTH = 5,
    parameter INDEX_WIDTH = 3,
    parameter TAG_WIDTH = DATA_WIDTH - INDEX_WIDTH - BLOCK_OFFSET_WIDTH - DATA_PER_BYTE_WIDTH
)(
    input clk,
    input rst_n,


    input [`DATA_BUS] addr,
    input enable,
    input rw,

    input [`DATA_BUS] write,
    input [2**DATA_PER_BYTE_WIDTH-1:0] mem_sel,

    output reg [`DATA_BUS] read,
    output reg rw_valid = 0,

    output ready,


    output reg [`DATA_BUS] mem_addr,
    output reg mem_enable,
    output reg mem_rw,

    input [`DATA_BUS] mem_read,
    input mem_read_valid,

    output reg [`DATA_BUS] mem_write,
    input mem_write_req_input,

    
    input mem_last
    );



    localparam ASSOCIATIVITY = 1 << ASSO_WIDTH;
    localparam BLOCK_SIZE = 1 << BLOCK_OFFSET_WIDTH;
    localparam INDEX_SIZE = 1 << INDEX_WIDTH;
    localparam TAG_SIZE = 1 << TAG_WIDTH;


    localparam STATE_READY = 0;
    localparam STATE_PAUSE = 1;
    localparam STATE_POPULATE = 2;
    localparam STATE_WRITEOUT = 3;


    integer i, j, k;
    
    reg [1:0] state;
    reg rw_internal;


    wire [TAG_WIDTH         -1:0] tag  = addr[DATA_WIDTH-1:INDEX_WIDTH+BLOCK_OFFSET_WIDTH+DATA_PER_BYTE_WIDTH];
    wire [INDEX_WIDTH       -1:0] block_index  = addr[INDEX_WIDTH+BLOCK_OFFSET_WIDTH+DATA_PER_BYTE_WIDTH-1:BLOCK_OFFSET_WIDTH+DATA_PER_BYTE_WIDTH];
    wire [BLOCK_OFFSET_WIDTH-1:0] block_offset = addr[BLOCK_OFFSET_WIDTH+DATA_PER_BYTE_WIDTH-1:DATA_PER_BYTE_WIDTH];
    reg  [ASSO_WIDTH        -1:0] location;
    

    reg                           rw_reg;
    reg  [DATA_WIDTH        -1:0] addr_reg;
    reg  [TAG_WIDTH         -1:0] tag_reg;
    reg  [INDEX_WIDTH       -1:0] block_index_reg;
    reg  [BLOCK_OFFSET_WIDTH-1:0] block_offset_reg;
    reg  [ASSO_WIDTH        -1:0] location_reg;
    reg  [DATA_WIDTH        -1:0] write_reg;
    reg  [                   3:0] mem_sel_reg;

    reg [BLOCK_OFFSET_WIDTH-1:0] cnt;


    reg [TAG_WIDTH-1:0] tags[0:INDEX_SIZE-1][0:ASSOCIATIVITY-1];
    reg [`DATA_BUS] blocks[0:INDEX_SIZE-1][0:ASSOCIATIVITY-1][0:BLOCK_SIZE-1];
    reg valid[0:INDEX_SIZE-1][0:ASSOCIATIVITY-1];
    reg dirty[0:INDEX_SIZE-1][0:ASSOCIATIVITY-1];
    
    assign ready = state == STATE_READY;

    wire cache_hit = enable && valid[block_index][location] && tags[block_index][location] === tag && !rw_valid;
    wire cache_read_hit = cache_hit && rw == `MEM_READ && state == STATE_READY;
    wire cache_write_hit = cache_hit && rw == `MEM_WRITE && state == STATE_READY;
    wire cache_miss = !cache_hit && enable && !rw_valid;

    wire populate = rw_reg == `MEM_WRITE && mem_read_valid && cnt == block_offset_reg && state == STATE_POPULATE;
    wire [`DATA_BUS] populate_data = populate ? write_reg : mem_read;
        
    localparam LOCATION_X = {ASSO_WIDTH{1'bx}};
    localparam TAG_X = {TAG_WIDTH{1'bx}};


    always @*
    begin
        location = LOCATION_X;
        for (i = 0; i < ASSOCIATIVITY; i = i + 1)
            if (tags[block_index][i] == tag)
                location = i;
        if (location === LOCATION_X)
            for (i = 0; i < ASSOCIATIVITY; i = i + 1)
                if (tags[block_index][i] === TAG_X)
                    location = i;
        if (location === LOCATION_X)
            location = 0;
    end
    
    task write_task;
        input [`DATA_BUS] addr;
        input [INDEX_WIDTH       -1:0] block_index;
        input [BLOCK_OFFSET_WIDTH-1:0] block_offset;
        input [ASSO_WIDTH        -1:0] location;
        input                    [3:0] mem_sel;
    
        input [`DATA_BUS] write;
    
        begin
            if (mem_sel[3])
                blocks[block_index][location][block_offset][31:24] <= write[31:24];
            if (mem_sel[2])
                blocks[block_index][location][block_offset][23:16] <= write[23:16];
            if (mem_sel[1])
                blocks[block_index][location][block_offset][15: 8] <= write[15: 8];
            if (mem_sel[0])
                blocks[block_index][location][block_offset][ 7: 0] <= write[ 7: 0];

            $display("Data cache write %h on addr %h in mode %b", write, addr, mem_sel);
        end
    endtask
    
    task read_task;
        input [`DATA_BUS] addr;
        input [`DATA_BUS] data;
        
        begin
            read <= data;
            $display("Data cache read %h on addr %h", data, addr);
        end
    endtask

    always @(posedge clk, negedge rst_n)
    begin
        if (!rst_n)
        begin
            state <= STATE_READY;
            mem_enable <= `FALSE;
            
            rw_valid <= `FALSE;
            
            block_offset_reg <= 0;
            block_index_reg <= 0;
            tag_reg <= 0;
            mem_sel_reg <= 0;
            location_reg <= 0;
            rw_reg <= 0;
            write_reg <= 0;
            cnt <= 0;
            mem_rw <= 0;
            mem_addr <= 0;
            mem_write <= 0;
            read <= 0;
            
            for (i = 0; i < INDEX_SIZE; i = i + 1)
                for (j = 0; j < ASSOCIATIVITY; j = j + 1)
                begin
                    tags[i][j] <= TAG_X;
                    valid[i][j] <= 0;
                    dirty[i][j] <= 0;
                    for (k = 0; k < BLOCK_SIZE; k = k + 1)
                        blocks[i][j][k] <= 0;
                end
        end
        else
        begin
            rw_valid <= `FALSE;

            case (state)
                STATE_READY: begin
                    if (cache_read_hit)
                    begin
                        rw_reg <= `MEM_READ;
                        rw_valid <= `TRUE;
                        read_task(addr, blocks[block_index][location][block_offset]);
                    end
                    else if (cache_write_hit)
                    begin
                        rw_reg <= `MEM_WRITE;
                        rw_valid <= `TRUE;
                        dirty[block_index][location] <= `TRUE;
                        read <= 0;
                        write_task(addr, block_index, block_offset, location, mem_sel, write);
                    end
                    else if (cache_miss)
                    begin
                        mem_enable <= `TRUE;

                        block_offset_reg <= block_offset;
                        block_index_reg <= block_index;
                        mem_sel_reg <= mem_sel;
                        tag_reg <= tag;
                        location_reg <= location;
                        addr_reg <= addr;
                        rw_reg <= rw;
                        write_reg <= write;




                        if (!valid[block_index][location] || !dirty[block_index][location])
                        begin
                            cnt <= 0;
                            state <= STATE_POPULATE;
                            mem_rw <= `MEM_READ;
                            mem_addr <= {tag, block_index, {BLOCK_OFFSET_WIDTH+DATA_PER_BYTE_WIDTH{1'b0}}};
                            
`ifdef DEBUG_DATA
                            $display("Data cache miss on unchanged block %x", addr);
`endif
                        end

                        else if (dirty[block_index][location])
                        begin
                            cnt <= 0;
                            state <= STATE_WRITEOUT;
                            mem_rw <= `MEM_WRITE;
                            mem_addr <= {tags[block_index][location], block_index, {BLOCK_OFFSET_WIDTH+DATA_PER_BYTE_WIDTH{1'b0}}};
                            
 `ifdef DEBUG_DATA
                            $display("Data cache miss on changed block %x", addr);
 `endif
                        end
                    end
                end

                STATE_WRITEOUT: begin
                    if (mem_write_req_input)
                    begin
                        mem_write <= blocks[block_index_reg][location_reg][cnt + 1];

                        cnt <= cnt + 1;
                    end
                    else
                    begin
                        if (mem_last)
                        begin
                            cnt <= 0;


                            state <= STATE_POPULATE;
                            mem_enable <= `TRUE;
                            mem_rw <= `MEM_READ;
                            mem_addr <= {tag_reg, block_index_reg, {BLOCK_OFFSET_WIDTH{1'b0}}, {DATA_PER_BYTE_WIDTH{1'b0}}};
                            dirty[block_index_reg][location_reg] <= `FALSE;
                            mem_write <= 'bx;
                        end
                        else if (cnt == 0)
                            mem_write <= blocks[block_index_reg][location_reg][cnt];
                    end
                end


                STATE_POPULATE: begin
                    if (mem_read_valid)
                    begin
                        blocks[block_index_reg][location_reg][cnt] <= populate_data;

                        if (cnt == block_offset_reg && rw_reg == `MEM_READ)
                        begin
                            read_task(addr_reg, populate_data);
`ifdef DEBUG_DATA
                            $display("Data cache populated addr %x, data %x", addr_reg, populate_data);
`endif
                        end

                        cnt <= cnt + 1;

                        if (mem_last)
                        begin
                            tags[block_index_reg][location_reg] <= tag_reg;
                            valid[block_index_reg][location_reg] <= `TRUE;
                            mem_enable <= `FALSE;

                            if (rw_reg == `MEM_WRITE)
                            begin
                                dirty[block_index_reg][location_reg] <= `TRUE;
                                write_task(addr_reg, block_index_reg, block_offset_reg, location_reg, mem_sel_reg, write_reg);
                            end
                            else
                                dirty[block_index_reg][location_reg] <= `FALSE;

                            state <= STATE_PAUSE;
                            block_offset_reg <= BLOCK_SIZE;
                        end
                    end
                end


                STATE_PAUSE: begin
                    rw_valid <= `TRUE;
                    state <= STATE_READY;
                end
            endcase
        end
    end
    
endmodule