data/full_repos/permissive/463204869/units/tcu/source/rtl/verilog/tcu_priv_tlb.v
1: b'%Error: data/full_repos/permissive/463204869/units/tcu/source/rtl/verilog/tcu_priv_tlb.v:3: Cannot find include file: tcu_parameter.vh\n    `include "tcu_parameter.vh" \n             ^~~~~~~~~~~~~~~~~~\n        ... Looked in:\n             data/full_repos/permissive/463204869/units/tcu/source/rtl/verilog,data/full_repos/permissive/463204869/tcu_parameter.vh\n             data/full_repos/permissive/463204869/units/tcu/source/rtl/verilog,data/full_repos/permissive/463204869/tcu_parameter.vh.v\n             data/full_repos/permissive/463204869/units/tcu/source/rtl/verilog,data/full_repos/permissive/463204869/tcu_parameter.vh.sv\n             tcu_parameter.vh\n             tcu_parameter.vh.v\n             tcu_parameter.vh.sv\n             obj_dir/tcu_parameter.vh\n             obj_dir/tcu_parameter.vh.v\n             obj_dir/tcu_parameter.vh.sv\n%Error: data/full_repos/permissive/463204869/units/tcu/source/rtl/verilog/tcu_priv_tlb.v:4: syntax error, unexpected \',\', expecting \'[\'\n    ,parameter TLB_DEPTH          = 32,  \n    ^\n%Error: data/full_repos/permissive/463204869/units/tcu/source/rtl/verilog/tcu_priv_tlb.v:5: syntax error, unexpected parameter, expecting IDENTIFIER\n    parameter TCU_ENABLE_VIRT_PES = 0\n    ^~~~~~~~~\n%Error: data/full_repos/permissive/463204869/units/tcu/source/rtl/verilog/tcu_priv_tlb.v:6: syntax error, unexpected \')\', expecting \';\'\n)(\n^\n%Error: data/full_repos/permissive/463204869/units/tcu/source/rtl/verilog/tcu_priv_tlb.v:8: syntax error, unexpected input, expecting IDENTIFIER or do or final\n    input  wire                          reset_n_i,\n    ^~~~~\n%Error: data/full_repos/permissive/463204869/units/tcu/source/rtl/verilog/tcu_priv_tlb.v:10: syntax error, unexpected input, expecting IDENTIFIER or do or final\n    input  wire                          tcu_features_virt_pes_i,\n    ^~~~~\n%Error: data/full_repos/permissive/463204869/units/tcu/source/rtl/verilog/tcu_priv_tlb.v:12: syntax error, unexpected input, expecting IDENTIFIER or do or final\n    input  wire                          tlb_en_i,\n    ^~~~~\n%Error: data/full_repos/permissive/463204869/units/tcu/source/rtl/verilog/tcu_priv_tlb.v:13: syntax error, unexpected input, expecting IDENTIFIER or do or final\n    input  wire   [TCU_TLB_CMD_SIZE-1:0] tlb_cmd_i,\n    ^~~~~\n%Error: data/full_repos/permissive/463204869/units/tcu/source/rtl/verilog/tcu_priv_tlb.v:14: syntax error, unexpected input, expecting IDENTIFIER or do or final\n    input  wire  [TCU_TLB_DATA_SIZE-1:0] tlb_wdata_i,    \n    ^~~~~\n%Error: data/full_repos/permissive/463204869/units/tcu/source/rtl/verilog/tcu_priv_tlb.v:15: syntax error, unexpected output, expecting IDENTIFIER or do or final\n    output wire  [TCU_TLB_DATA_SIZE-1:0] tlb_rdata_o,    \n    ^~~~~~\n%Error: data/full_repos/permissive/463204869/units/tcu/source/rtl/verilog/tcu_priv_tlb.v:16: syntax error, unexpected output, expecting IDENTIFIER or do or final\n    output wire                          tlb_active_o,\n    ^~~~~~\n%Error: data/full_repos/permissive/463204869/units/tcu/source/rtl/verilog/tcu_priv_tlb.v:17: syntax error, unexpected output, expecting IDENTIFIER or do or final\n    output wire                          tlb_done_o,\n    ^~~~~~\n%Error: data/full_repos/permissive/463204869/units/tcu/source/rtl/verilog/tcu_priv_tlb.v:18: syntax error, unexpected output, expecting IDENTIFIER or do or final\n    output wire     [TCU_ERROR_SIZE-1:0] tlb_error_o\n    ^~~~~~\n%Error: data/full_repos/permissive/463204869/units/tcu/source/rtl/verilog/tcu_priv_tlb.v:19: syntax error, unexpected \')\', expecting \',\' or \';\'\n);\n^\n%Error: data/full_repos/permissive/463204869/units/tcu/source/rtl/verilog/tcu_priv_tlb.v:127: syntax error, unexpected always\n    always @(posedge clk_i) begin\n    ^~~~~~\n%Error: Exiting due to 15 error(s)\n'
module tcu_priv_tlb #(
    `include "tcu_parameter.vh"
    ,parameter TLB_DEPTH          = 32,
    parameter TCU_ENABLE_VIRT_PES = 0
)(
    input  wire                          clk_i,
    input  wire                          reset_n_i,

    input  wire                          tcu_features_virt_pes_i,

    input  wire                          tlb_en_i,
    input  wire   [TCU_TLB_CMD_SIZE-1:0] tlb_cmd_i,
    input  wire  [TCU_TLB_DATA_SIZE-1:0] tlb_wdata_i,
    output wire  [TCU_TLB_DATA_SIZE-1:0] tlb_rdata_o,
    output wire                          tlb_active_o,
    output wire                          tlb_done_o,
    output wire     [TCU_ERROR_SIZE-1:0] tlb_error_o
);

    localparam TLB_ADDR_WIDTH = $clog2(TLB_DEPTH);

    localparam TLB_STATES_SIZE       = 4;
    localparam S_TLB_IDLE            = 4'h0;
    localparam S_TLB_WRITE_START     = 4'h1;
    localparam S_TLB_WRITE_OCCUP     = 4'h2;
    localparam S_TLB_WRITE_OVERWRITE = 4'h3;
    localparam S_TLB_READ_START      = 4'h4;
    localparam S_TLB_READ_CHECK      = 4'h5;
    localparam S_TLB_DEL_START       = 4'h6;
    localparam S_TLB_DEL_CHECK       = 4'h7;
    localparam S_TLB_DEL_REMOVE      = 4'h8;
    localparam S_TLB_CLEAR_START     = 4'h9;
    localparam S_TLB_CLEAR_CHECK     = 4'hA;
    localparam S_TLB_CLEAR_REMOVE    = 4'hB;
    localparam S_TLB_FINISH          = 4'hF;

    reg [TLB_STATES_SIZE-1:0] tlb_state, next_tlb_state;


    function automatic [3:0] ctz8;
        input [7:0] data;
        begin
            casex(data)
                8'bxxxx_xxx1: ctz8 = 3'd0;
                8'bxxxx_xx10: ctz8 = 3'd1;
                8'bxxxx_x100: ctz8 = 3'd2;
                8'bxxxx_1000: ctz8 = 3'd3;
                8'bxxx1_0000: ctz8 = 3'd4;
                8'bxx10_0000: ctz8 = 3'd5;
                8'bx100_0000: ctz8 = 3'd6;
                8'b1000_0000: ctz8 = 3'd7;
                default: ctz8 = 8'd8;
            endcase
        end
    endfunction


    function automatic [3:0] clz8;
        input [7:0] data;
        begin
            casex(data)
                8'b1xxx_xxxx: clz8 = 3'd0;
                8'b01xx_xxxx: clz8 = 3'd1;
                8'b001x_xxxx: clz8 = 3'd2;
                8'b0001_xxxx: clz8 = 3'd3;
                8'b0000_1xxx: clz8 = 3'd4;
                8'b0000_01xx: clz8 = 3'd5;
                8'b0000_001x: clz8 = 3'd6;
                8'b0000_0001: clz8 = 3'd7;
                default: clz8 = 8'd8;
            endcase
        end
    endfunction



    reg [TLB_DEPTH-1:0] r_tlb_valid_entry, rin_tlb_valid_entry;

    reg    [TLB_ADDR_WIDTH-1:0] r_tlb_mem_addr, rin_tlb_mem_addr;
    reg [TCU_TLB_DATA_SIZE-1:0] r_tlb_mem_rdata, rin_tlb_mem_rdata;

    reg [TLB_ADDR_WIDTH-1:0] first_valid_entry;
    reg [TLB_ADDR_WIDTH-1:0] last_valid_entry;

    reg r2_tlb_valid_entry_bit;

    reg tlb_mem_en;
    reg tlb_mem_we;


    reg [TCU_ERROR_SIZE-1:0] r_tlb_error, rin_tlb_error;



    wire    [TCU_TLB_VPEID_SIZE-1:0] tlb_wdata_vpeid    = tlb_wdata_i[TCU_TLB_VPEID_SIZE-1 : 0];
    wire [TCU_TLB_VIRTPAGE_SIZE-1:0] tlb_wdata_virtpage = tlb_wdata_i[TCU_TLB_VIRTPAGE_SIZE+TCU_TLB_VPEID_SIZE-1 : TCU_TLB_VPEID_SIZE];
    wire [TCU_TLB_PHYSPAGE_SIZE-1:0] tlb_wdata_physpage = tlb_wdata_i[TCU_TLB_PHYSPAGE_SIZE+TCU_TLB_VIRTPAGE_SIZE+TCU_TLB_VPEID_SIZE-1 : TCU_TLB_VIRTPAGE_SIZE+TCU_TLB_VPEID_SIZE];
    wire     [TCU_TLB_PERM_SIZE-1:0] tlb_wdata_perm     = tlb_wdata_i[TCU_TLB_PERM_SIZE+TCU_TLB_PHYSPAGE_SIZE+TCU_TLB_VIRTPAGE_SIZE+TCU_TLB_VPEID_SIZE-1 : TCU_TLB_PHYSPAGE_SIZE+TCU_TLB_VIRTPAGE_SIZE+TCU_TLB_VPEID_SIZE];


    wire [TCU_TLB_DATA_SIZE-1:0] tlb_mem_rdata;

    wire    [TCU_TLB_VPEID_SIZE-1:0] tlb_rdata_vpeid    = tlb_mem_rdata[TCU_TLB_VPEID_SIZE-1 : 0];
    wire [TCU_TLB_VIRTPAGE_SIZE-1:0] tlb_rdata_virtpage = tlb_mem_rdata[TCU_TLB_VIRTPAGE_SIZE+TCU_TLB_VPEID_SIZE-1 : TCU_TLB_VPEID_SIZE];
    wire [TCU_TLB_PHYSPAGE_SIZE-1:0] tlb_rdata_physpage = tlb_mem_rdata[TCU_TLB_PHYSPAGE_SIZE+TCU_TLB_VIRTPAGE_SIZE+TCU_TLB_VPEID_SIZE-1 : TCU_TLB_VIRTPAGE_SIZE+TCU_TLB_VPEID_SIZE];
    wire     [TCU_TLB_PERM_SIZE-1:0] tlb_rdata_perm     = tlb_mem_rdata[TCU_TLB_PERM_SIZE+TCU_TLB_PHYSPAGE_SIZE+TCU_TLB_VIRTPAGE_SIZE+TCU_TLB_VPEID_SIZE-1 : TCU_TLB_PHYSPAGE_SIZE+TCU_TLB_VIRTPAGE_SIZE+TCU_TLB_VPEID_SIZE];


    wire tlb_empty = !r_tlb_valid_entry;

    wire [TLB_ADDR_WIDTH-1:0] last_valid_entry_incr = last_valid_entry + 1;

    wire [3:0] ctz_valid_entry_0 = ctz8(r_tlb_valid_entry[ 7: 0]);
    wire [3:0] ctz_valid_entry_1 = ctz8(r_tlb_valid_entry[15: 8]);
    wire [3:0] ctz_valid_entry_2 = ctz8(r_tlb_valid_entry[23:16]);
    wire [3:0] ctz_valid_entry_3 = ctz8(r_tlb_valid_entry[31:24]);

    wire [3:0] clz_valid_entry_0 = clz8(r_tlb_valid_entry[ 7: 0]);
    wire [3:0] clz_valid_entry_1 = clz8(r_tlb_valid_entry[15: 8]);
    wire [3:0] clz_valid_entry_2 = clz8(r_tlb_valid_entry[23:16]);
    wire [3:0] clz_valid_entry_3 = clz8(r_tlb_valid_entry[31:24]);




    always @(posedge clk_i) begin
        if (reset_n_i == 1'b0) begin
            tlb_state <= S_TLB_IDLE;

            r_tlb_valid_entry <= {TLB_DEPTH{1'b0}};
            r2_tlb_valid_entry_bit <= 1'b0;

            r_tlb_mem_addr <= {TLB_ADDR_WIDTH{1'b0}};
            r_tlb_mem_rdata <= {TCU_TLB_DATA_SIZE{1'b0}};

            r_tlb_error <= TCU_ERROR_NONE;
        end
        else begin
            tlb_state <= next_tlb_state;

            r_tlb_valid_entry <= rin_tlb_valid_entry;
            r2_tlb_valid_entry_bit <= r_tlb_valid_entry[r_tlb_mem_addr];

            r_tlb_mem_addr <= rin_tlb_mem_addr;
            r_tlb_mem_rdata <= rin_tlb_mem_rdata;

            r_tlb_error <= rin_tlb_error;
        end
    end





    always @* begin

        if (!ctz_valid_entry_0[3]) begin
            first_valid_entry = ctz_valid_entry_0[2:0];
        end
        else if (!ctz_valid_entry_1[3]) begin
            first_valid_entry = {1'b1, ctz_valid_entry_1[2:0]};
        end
        else if (!ctz_valid_entry_2[3]) begin
            first_valid_entry = {2'h2, ctz_valid_entry_2[2:0]};
        end
        else if (!ctz_valid_entry_3[3]) begin
            first_valid_entry = {2'h3, ctz_valid_entry_3[2:0]};
        end
        else begin
            first_valid_entry = {TLB_ADDR_WIDTH{1'b0}};
        end
    end



    always @* begin

        if (!clz_valid_entry_3[3]) begin
            last_valid_entry = TLB_DEPTH - clz_valid_entry_3[2:0] - 1;
        end
        else if (!clz_valid_entry_2[3]) begin
            last_valid_entry = TLB_DEPTH - {1'b1, clz_valid_entry_2[2:0]} - 1;
        end
        else if (!clz_valid_entry_1[3]) begin
            last_valid_entry = TLB_DEPTH - {2'h2, clz_valid_entry_1[2:0]} - 1;
        end
        else if (!clz_valid_entry_0[3]) begin
            last_valid_entry = TLB_DEPTH - {2'h3, clz_valid_entry_0[2:0]} - 1;
        end
        else begin
            last_valid_entry = {TLB_ADDR_WIDTH{1'b0}};
        end
    end





    always @* begin
        next_tlb_state = tlb_state;

        rin_tlb_valid_entry = r_tlb_valid_entry;

        rin_tlb_mem_addr = r_tlb_mem_addr;
        rin_tlb_mem_rdata = {TCU_TLB_DATA_SIZE{1'b0}};

        tlb_mem_en = 1'b0;
        tlb_mem_we = 1'b0;

        rin_tlb_error = r_tlb_error;


        case(tlb_state)

            S_TLB_IDLE: begin
                if (tlb_en_i) begin
                    rin_tlb_error = TCU_ERROR_NONE;

                    case(tlb_cmd_i)
                        TCU_TLB_CMD_WRITE_ENTRY: begin
                            rin_tlb_mem_addr = tlb_empty ? {TLB_ADDR_WIDTH{1'b0}} : last_valid_entry_incr;
                            next_tlb_state = S_TLB_WRITE_START;
                        end

                        TCU_TLB_CMD_READ_ENTRY: begin
                            rin_tlb_mem_addr = last_valid_entry;
                            next_tlb_state = S_TLB_READ_START;
                        end

                        TCU_TLB_CMD_DEL_ENTRY: begin
                            rin_tlb_mem_addr = last_valid_entry;
                            next_tlb_state = S_TLB_DEL_START;
                        end

                        TCU_TLB_CMD_CLEAR: begin
                            rin_tlb_mem_addr = first_valid_entry;
                            next_tlb_state = S_TLB_CLEAR_START;
                        end
                    endcase
                end
            end




            S_TLB_WRITE_START: begin
                tlb_mem_en = 1'b1;


                if (!r_tlb_valid_entry[r_tlb_mem_addr]) begin
                    tlb_mem_we = 1'b1;
                    rin_tlb_valid_entry[r_tlb_mem_addr] = 1'b1;
                    next_tlb_state = S_TLB_FINISH;
                end


                else begin
                    rin_tlb_mem_addr = r_tlb_mem_addr + 1;
                    next_tlb_state = S_TLB_WRITE_OCCUP;
                end
            end

            S_TLB_WRITE_OCCUP: begin

                tlb_mem_en = 1'b1;


                if (tlb_rdata_perm[TCU_TLB_PERM_SIZE-1]) begin

                    if (r_tlb_mem_addr == last_valid_entry_incr) begin
                        rin_tlb_error = TCU_ERROR_TLB_FULL;
                        next_tlb_state = S_TLB_FINISH;
                    end


                    else if (r_tlb_valid_entry[r_tlb_mem_addr]) begin
                        rin_tlb_mem_addr = r_tlb_mem_addr + 1;
                    end


                    else begin
                        next_tlb_state = S_TLB_WRITE_OVERWRITE;
                    end
                end


                else begin
                    rin_tlb_mem_addr = r_tlb_mem_addr - 1;
                    next_tlb_state = S_TLB_WRITE_OVERWRITE;
                end
            end

            S_TLB_WRITE_OVERWRITE: begin
                tlb_mem_en = 1'b1;
                tlb_mem_we = 1'b1;
                rin_tlb_valid_entry[r_tlb_mem_addr] = 1'b1;
                next_tlb_state = S_TLB_FINISH;
            end




            S_TLB_READ_START: begin
                tlb_mem_en = 1'b1;


                if (r_tlb_valid_entry[r_tlb_mem_addr]) begin
                    rin_tlb_mem_addr = r_tlb_mem_addr - 1;
                    next_tlb_state = S_TLB_READ_CHECK;
                end


                else begin
                    rin_tlb_error = TCU_ERROR_TLB_MISS;
                    next_tlb_state = S_TLB_FINISH;
                end
            end


            S_TLB_READ_CHECK: begin
                rin_tlb_mem_rdata = tlb_mem_rdata;


                if (r_tlb_valid_entry[r_tlb_mem_addr]) begin
                    tlb_mem_en = 1'b1;
                end


                if (r2_tlb_valid_entry_bit &&
                    (!(TCU_ENABLE_VIRT_PES && tcu_features_virt_pes_i) || (tlb_rdata_vpeid == tlb_wdata_vpeid)) &&
                    (tlb_rdata_virtpage == tlb_wdata_virtpage)) begin


                    if (tlb_rdata_perm[1:0] & tlb_wdata_perm[1:0]) begin

                        next_tlb_state = S_TLB_FINISH;
                    end


                    else begin
                        rin_tlb_error = TCU_ERROR_NO_PERM;
                        next_tlb_state = S_TLB_FINISH;
                    end
                end


                else if ((r_tlb_mem_addr >= first_valid_entry) && (r_tlb_mem_addr < last_valid_entry)) begin
                    rin_tlb_mem_addr = r_tlb_mem_addr - 1;
                end


                else begin
                    rin_tlb_error = TCU_ERROR_TLB_MISS;
                    next_tlb_state = S_TLB_FINISH;
                end
            end




            S_TLB_DEL_START: begin
                tlb_mem_en = 1'b1;


                if (r_tlb_valid_entry[r_tlb_mem_addr]) begin
                    rin_tlb_mem_addr = r_tlb_mem_addr - 1;
                    next_tlb_state = S_TLB_DEL_CHECK;
                end


                else begin
                    rin_tlb_error = TCU_ERROR_TLB_MISS;
                    next_tlb_state = S_TLB_FINISH;
                end
            end


            S_TLB_DEL_CHECK: begin

                if (r_tlb_valid_entry[r_tlb_mem_addr]) begin
                    tlb_mem_en = 1'b1;
                end


                if (r2_tlb_valid_entry_bit &&
                    (!(TCU_ENABLE_VIRT_PES && tcu_features_virt_pes_i) || (tlb_rdata_vpeid == tlb_wdata_vpeid)) &&
                    (tlb_rdata_virtpage == tlb_wdata_virtpage)) begin

                    rin_tlb_mem_addr = r_tlb_mem_addr + 1;
                    next_tlb_state = S_TLB_DEL_REMOVE;
                end


                else if ((r_tlb_mem_addr >= first_valid_entry) && (r_tlb_mem_addr < last_valid_entry)) begin
                    rin_tlb_mem_addr = r_tlb_mem_addr - 1;
                end


                else begin
                    rin_tlb_error = TCU_ERROR_TLB_MISS;
                    next_tlb_state = S_TLB_FINISH;
                end
            end

            S_TLB_DEL_REMOVE: begin
                rin_tlb_valid_entry[r_tlb_mem_addr] = 1'b0;
                next_tlb_state = S_TLB_FINISH;
            end




            S_TLB_CLEAR_START: begin
                tlb_mem_en = 1'b1;
                rin_tlb_mem_addr = r_tlb_mem_addr + 1;
                next_tlb_state = S_TLB_CLEAR_CHECK;
            end


            S_TLB_CLEAR_CHECK: begin

                if (r_tlb_valid_entry[r_tlb_mem_addr]) begin
                    tlb_mem_en = 1'b1;
                end


                if (r2_tlb_valid_entry_bit && !tlb_rdata_perm[TCU_TLB_PERM_SIZE-1]) begin
                    rin_tlb_mem_addr = r_tlb_mem_addr - 1;
                    next_tlb_state = S_TLB_CLEAR_REMOVE;
                end


                else if ((r_tlb_mem_addr >= first_valid_entry) && (r_tlb_mem_addr < last_valid_entry)) begin
                    rin_tlb_mem_addr = r_tlb_mem_addr + 1;
                end


                else begin
                    next_tlb_state = S_TLB_FINISH;
                end
            end

            S_TLB_CLEAR_REMOVE: begin

                rin_tlb_mem_addr = r_tlb_mem_addr + 1;

                rin_tlb_valid_entry[r_tlb_mem_addr] = 1'b0;
                next_tlb_state = S_TLB_CLEAR_START;
            end


            S_TLB_FINISH: begin
                next_tlb_state = S_TLB_IDLE;
            end


            default: next_tlb_state = tlb_state;
        endcase
    end


    assign tlb_rdata_o = r_tlb_mem_rdata;
    assign tlb_active_o = (tlb_state != S_TLB_IDLE);
    assign tlb_done_o = (tlb_state == S_TLB_FINISH);
    assign tlb_error_o = r_tlb_error;


    mem_sp_wrap #(
        .MEM_TYPE("distributed"),
        .MEM_DATAWIDTH(TCU_TLB_DATA_SIZE),
        .MEM_ADDRWIDTH(TLB_ADDR_WIDTH)
    ) tlb_mem (
        .clk    (clk_i),
        .reset  (~reset_n_i),
        .en     (tlb_mem_en),
        .we     ({((TCU_TLB_DATA_SIZE+7)/8){tlb_mem_we}}),
        .addr   (r_tlb_mem_addr),
        .din    (tlb_wdata_i),
        .dout   (tlb_mem_rdata)
    );



endmodule
module tcu_priv_tlb #(
    `include "tcu_parameter.vh"
    ,parameter TLB_DEPTH          = 32,
    parameter TCU_ENABLE_VIRT_PES = 0
)(
    input  wire                          clk_i,
    input  wire                          reset_n_i,

    input  wire                          tcu_features_virt_pes_i,

    input  wire                          tlb_en_i,
    input  wire   [TCU_TLB_CMD_SIZE-1:0] tlb_cmd_i,
    input  wire  [TCU_TLB_DATA_SIZE-1:0] tlb_wdata_i,
    output wire  [TCU_TLB_DATA_SIZE-1:0] tlb_rdata_o,
    output wire                          tlb_active_o,
    output wire                          tlb_done_o,
    output wire     [TCU_ERROR_SIZE-1:0] tlb_error_o
);


    localparam TLB_ADDR_WIDTH = $clog2(TLB_DEPTH);

    localparam TLB_STATES_SIZE       = 4;
    localparam S_TLB_IDLE            = 4'h0;
    localparam S_TLB_WRITE_START     = 4'h1;
    localparam S_TLB_WRITE_OCCUP     = 4'h2;
    localparam S_TLB_WRITE_OVERWRITE = 4'h3;
    localparam S_TLB_READ_START      = 4'h4;
    localparam S_TLB_READ_CHECK      = 4'h5;
    localparam S_TLB_DEL_START       = 4'h6;
    localparam S_TLB_DEL_CHECK       = 4'h7;
    localparam S_TLB_DEL_REMOVE      = 4'h8;
    localparam S_TLB_CLEAR_START     = 4'h9;
    localparam S_TLB_CLEAR_CHECK     = 4'hA;
    localparam S_TLB_CLEAR_REMOVE    = 4'hB;
    localparam S_TLB_FINISH          = 4'hF;

    reg [TLB_STATES_SIZE-1:0] tlb_state, next_tlb_state;


    function automatic [3:0] ctz8;
        input [7:0] data;
        begin
            casex(data)
                8'bxxxx_xxx1: ctz8 = 3'd0;
                8'bxxxx_xx10: ctz8 = 3'd1;
                8'bxxxx_x100: ctz8 = 3'd2;
                8'bxxxx_1000: ctz8 = 3'd3;
                8'bxxx1_0000: ctz8 = 3'd4;
                8'bxx10_0000: ctz8 = 3'd5;
                8'bx100_0000: ctz8 = 3'd6;
                8'b1000_0000: ctz8 = 3'd7;
                default: ctz8 = 8'd8;
            endcase
        end
    endfunction


    function automatic [3:0] clz8;
        input [7:0] data;
        begin
            casex(data)
                8'b1xxx_xxxx: clz8 = 3'd0;
                8'b01xx_xxxx: clz8 = 3'd1;
                8'b001x_xxxx: clz8 = 3'd2;
                8'b0001_xxxx: clz8 = 3'd3;
                8'b0000_1xxx: clz8 = 3'd4;
                8'b0000_01xx: clz8 = 3'd5;
                8'b0000_001x: clz8 = 3'd6;
                8'b0000_0001: clz8 = 3'd7;
                default: clz8 = 8'd8;
            endcase
        end
    endfunction



    reg [TLB_DEPTH-1:0] r_tlb_valid_entry, rin_tlb_valid_entry;

    reg    [TLB_ADDR_WIDTH-1:0] r_tlb_mem_addr, rin_tlb_mem_addr;
    reg [TCU_TLB_DATA_SIZE-1:0] r_tlb_mem_rdata, rin_tlb_mem_rdata;

    reg [TLB_ADDR_WIDTH-1:0] first_valid_entry;
    reg [TLB_ADDR_WIDTH-1:0] last_valid_entry;

    reg r2_tlb_valid_entry_bit;

    reg tlb_mem_en;
    reg tlb_mem_we;


    reg [TCU_ERROR_SIZE-1:0] r_tlb_error, rin_tlb_error;



    wire    [TCU_TLB_VPEID_SIZE-1:0] tlb_wdata_vpeid    = tlb_wdata_i[TCU_TLB_VPEID_SIZE-1 : 0];
    wire [TCU_TLB_VIRTPAGE_SIZE-1:0] tlb_wdata_virtpage = tlb_wdata_i[TCU_TLB_VIRTPAGE_SIZE+TCU_TLB_VPEID_SIZE-1 : TCU_TLB_VPEID_SIZE];
    wire [TCU_TLB_PHYSPAGE_SIZE-1:0] tlb_wdata_physpage = tlb_wdata_i[TCU_TLB_PHYSPAGE_SIZE+TCU_TLB_VIRTPAGE_SIZE+TCU_TLB_VPEID_SIZE-1 : TCU_TLB_VIRTPAGE_SIZE+TCU_TLB_VPEID_SIZE];
    wire     [TCU_TLB_PERM_SIZE-1:0] tlb_wdata_perm     = tlb_wdata_i[TCU_TLB_PERM_SIZE+TCU_TLB_PHYSPAGE_SIZE+TCU_TLB_VIRTPAGE_SIZE+TCU_TLB_VPEID_SIZE-1 : TCU_TLB_PHYSPAGE_SIZE+TCU_TLB_VIRTPAGE_SIZE+TCU_TLB_VPEID_SIZE];


    wire [TCU_TLB_DATA_SIZE-1:0] tlb_mem_rdata;

    wire    [TCU_TLB_VPEID_SIZE-1:0] tlb_rdata_vpeid    = tlb_mem_rdata[TCU_TLB_VPEID_SIZE-1 : 0];
    wire [TCU_TLB_VIRTPAGE_SIZE-1:0] tlb_rdata_virtpage = tlb_mem_rdata[TCU_TLB_VIRTPAGE_SIZE+TCU_TLB_VPEID_SIZE-1 : TCU_TLB_VPEID_SIZE];
    wire [TCU_TLB_PHYSPAGE_SIZE-1:0] tlb_rdata_physpage = tlb_mem_rdata[TCU_TLB_PHYSPAGE_SIZE+TCU_TLB_VIRTPAGE_SIZE+TCU_TLB_VPEID_SIZE-1 : TCU_TLB_VIRTPAGE_SIZE+TCU_TLB_VPEID_SIZE];
    wire     [TCU_TLB_PERM_SIZE-1:0] tlb_rdata_perm     = tlb_mem_rdata[TCU_TLB_PERM_SIZE+TCU_TLB_PHYSPAGE_SIZE+TCU_TLB_VIRTPAGE_SIZE+TCU_TLB_VPEID_SIZE-1 : TCU_TLB_PHYSPAGE_SIZE+TCU_TLB_VIRTPAGE_SIZE+TCU_TLB_VPEID_SIZE];


    wire tlb_empty = !r_tlb_valid_entry;

    wire [TLB_ADDR_WIDTH-1:0] last_valid_entry_incr = last_valid_entry + 1;

    wire [3:0] ctz_valid_entry_0 = ctz8(r_tlb_valid_entry[ 7: 0]);
    wire [3:0] ctz_valid_entry_1 = ctz8(r_tlb_valid_entry[15: 8]);
    wire [3:0] ctz_valid_entry_2 = ctz8(r_tlb_valid_entry[23:16]);
    wire [3:0] ctz_valid_entry_3 = ctz8(r_tlb_valid_entry[31:24]);

    wire [3:0] clz_valid_entry_0 = clz8(r_tlb_valid_entry[ 7: 0]);
    wire [3:0] clz_valid_entry_1 = clz8(r_tlb_valid_entry[15: 8]);
    wire [3:0] clz_valid_entry_2 = clz8(r_tlb_valid_entry[23:16]);
    wire [3:0] clz_valid_entry_3 = clz8(r_tlb_valid_entry[31:24]);




    always @(posedge clk_i) begin
        if (reset_n_i == 1'b0) begin
            tlb_state <= S_TLB_IDLE;

            r_tlb_valid_entry <= {TLB_DEPTH{1'b0}};
            r2_tlb_valid_entry_bit <= 1'b0;

            r_tlb_mem_addr <= {TLB_ADDR_WIDTH{1'b0}};
            r_tlb_mem_rdata <= {TCU_TLB_DATA_SIZE{1'b0}};

            r_tlb_error <= TCU_ERROR_NONE;
        end
        else begin
            tlb_state <= next_tlb_state;

            r_tlb_valid_entry <= rin_tlb_valid_entry;
            r2_tlb_valid_entry_bit <= r_tlb_valid_entry[r_tlb_mem_addr];

            r_tlb_mem_addr <= rin_tlb_mem_addr;
            r_tlb_mem_rdata <= rin_tlb_mem_rdata;

            r_tlb_error <= rin_tlb_error;
        end
    end





    always @* begin

        if (!ctz_valid_entry_0[3]) begin
            first_valid_entry = ctz_valid_entry_0[2:0];
        end
        else if (!ctz_valid_entry_1[3]) begin
            first_valid_entry = {1'b1, ctz_valid_entry_1[2:0]};
        end
        else if (!ctz_valid_entry_2[3]) begin
            first_valid_entry = {2'h2, ctz_valid_entry_2[2:0]};
        end
        else if (!ctz_valid_entry_3[3]) begin
            first_valid_entry = {2'h3, ctz_valid_entry_3[2:0]};
        end
        else begin
            first_valid_entry = {TLB_ADDR_WIDTH{1'b0}};
        end
    end



    always @* begin

        if (!clz_valid_entry_3[3]) begin
            last_valid_entry = TLB_DEPTH - clz_valid_entry_3[2:0] - 1;
        end
        else if (!clz_valid_entry_2[3]) begin
            last_valid_entry = TLB_DEPTH - {1'b1, clz_valid_entry_2[2:0]} - 1;
        end
        else if (!clz_valid_entry_1[3]) begin
            last_valid_entry = TLB_DEPTH - {2'h2, clz_valid_entry_1[2:0]} - 1;
        end
        else if (!clz_valid_entry_0[3]) begin
            last_valid_entry = TLB_DEPTH - {2'h3, clz_valid_entry_0[2:0]} - 1;
        end
        else begin
            last_valid_entry = {TLB_ADDR_WIDTH{1'b0}};
        end
    end





    always @* begin
        next_tlb_state = tlb_state;

        rin_tlb_valid_entry = r_tlb_valid_entry;

        rin_tlb_mem_addr = r_tlb_mem_addr;
        rin_tlb_mem_rdata = {TCU_TLB_DATA_SIZE{1'b0}};

        tlb_mem_en = 1'b0;
        tlb_mem_we = 1'b0;

        rin_tlb_error = r_tlb_error;


        case(tlb_state)

            S_TLB_IDLE: begin
                if (tlb_en_i) begin
                    rin_tlb_error = TCU_ERROR_NONE;

                    case(tlb_cmd_i)
                        TCU_TLB_CMD_WRITE_ENTRY: begin
                            rin_tlb_mem_addr = tlb_empty ? {TLB_ADDR_WIDTH{1'b0}} : last_valid_entry_incr;
                            next_tlb_state = S_TLB_WRITE_START;
                        end

                        TCU_TLB_CMD_READ_ENTRY: begin
                            rin_tlb_mem_addr = last_valid_entry;
                            next_tlb_state = S_TLB_READ_START;
                        end

                        TCU_TLB_CMD_DEL_ENTRY: begin
                            rin_tlb_mem_addr = last_valid_entry;
                            next_tlb_state = S_TLB_DEL_START;
                        end

                        TCU_TLB_CMD_CLEAR: begin
                            rin_tlb_mem_addr = first_valid_entry;
                            next_tlb_state = S_TLB_CLEAR_START;
                        end
                    endcase
                end
            end




            S_TLB_WRITE_START: begin
                tlb_mem_en = 1'b1;


                if (!r_tlb_valid_entry[r_tlb_mem_addr]) begin
                    tlb_mem_we = 1'b1;
                    rin_tlb_valid_entry[r_tlb_mem_addr] = 1'b1;
                    next_tlb_state = S_TLB_FINISH;
                end


                else begin
                    rin_tlb_mem_addr = r_tlb_mem_addr + 1;
                    next_tlb_state = S_TLB_WRITE_OCCUP;
                end
            end

            S_TLB_WRITE_OCCUP: begin

                tlb_mem_en = 1'b1;


                if (tlb_rdata_perm[TCU_TLB_PERM_SIZE-1]) begin

                    if (r_tlb_mem_addr == last_valid_entry_incr) begin
                        rin_tlb_error = TCU_ERROR_TLB_FULL;
                        next_tlb_state = S_TLB_FINISH;
                    end


                    else if (r_tlb_valid_entry[r_tlb_mem_addr]) begin
                        rin_tlb_mem_addr = r_tlb_mem_addr + 1;
                    end


                    else begin
                        next_tlb_state = S_TLB_WRITE_OVERWRITE;
                    end
                end


                else begin
                    rin_tlb_mem_addr = r_tlb_mem_addr - 1;
                    next_tlb_state = S_TLB_WRITE_OVERWRITE;
                end
            end

            S_TLB_WRITE_OVERWRITE: begin
                tlb_mem_en = 1'b1;
                tlb_mem_we = 1'b1;
                rin_tlb_valid_entry[r_tlb_mem_addr] = 1'b1;
                next_tlb_state = S_TLB_FINISH;
            end




            S_TLB_READ_START: begin
                tlb_mem_en = 1'b1;


                if (r_tlb_valid_entry[r_tlb_mem_addr]) begin
                    rin_tlb_mem_addr = r_tlb_mem_addr - 1;
                    next_tlb_state = S_TLB_READ_CHECK;
                end


                else begin
                    rin_tlb_error = TCU_ERROR_TLB_MISS;
                    next_tlb_state = S_TLB_FINISH;
                end
            end


            S_TLB_READ_CHECK: begin
                rin_tlb_mem_rdata = tlb_mem_rdata;


                if (r_tlb_valid_entry[r_tlb_mem_addr]) begin
                    tlb_mem_en = 1'b1;
                end


                if (r2_tlb_valid_entry_bit &&
                    (!(TCU_ENABLE_VIRT_PES && tcu_features_virt_pes_i) || (tlb_rdata_vpeid == tlb_wdata_vpeid)) &&
                    (tlb_rdata_virtpage == tlb_wdata_virtpage)) begin


                    if (tlb_rdata_perm[1:0] & tlb_wdata_perm[1:0]) begin

                        next_tlb_state = S_TLB_FINISH;
                    end


                    else begin
                        rin_tlb_error = TCU_ERROR_NO_PERM;
                        next_tlb_state = S_TLB_FINISH;
                    end
                end


                else if ((r_tlb_mem_addr >= first_valid_entry) && (r_tlb_mem_addr < last_valid_entry)) begin
                    rin_tlb_mem_addr = r_tlb_mem_addr - 1;
                end


                else begin
                    rin_tlb_error = TCU_ERROR_TLB_MISS;
                    next_tlb_state = S_TLB_FINISH;
                end
            end




            S_TLB_DEL_START: begin
                tlb_mem_en = 1'b1;


                if (r_tlb_valid_entry[r_tlb_mem_addr]) begin
                    rin_tlb_mem_addr = r_tlb_mem_addr - 1;
                    next_tlb_state = S_TLB_DEL_CHECK;
                end


                else begin
                    rin_tlb_error = TCU_ERROR_TLB_MISS;
                    next_tlb_state = S_TLB_FINISH;
                end
            end


            S_TLB_DEL_CHECK: begin

                if (r_tlb_valid_entry[r_tlb_mem_addr]) begin
                    tlb_mem_en = 1'b1;
                end


                if (r2_tlb_valid_entry_bit &&
                    (!(TCU_ENABLE_VIRT_PES && tcu_features_virt_pes_i) || (tlb_rdata_vpeid == tlb_wdata_vpeid)) &&
                    (tlb_rdata_virtpage == tlb_wdata_virtpage)) begin

                    rin_tlb_mem_addr = r_tlb_mem_addr + 1;
                    next_tlb_state = S_TLB_DEL_REMOVE;
                end


                else if ((r_tlb_mem_addr >= first_valid_entry) && (r_tlb_mem_addr < last_valid_entry)) begin
                    rin_tlb_mem_addr = r_tlb_mem_addr - 1;
                end


                else begin
                    rin_tlb_error = TCU_ERROR_TLB_MISS;
                    next_tlb_state = S_TLB_FINISH;
                end
            end

            S_TLB_DEL_REMOVE: begin
                rin_tlb_valid_entry[r_tlb_mem_addr] = 1'b0;
                next_tlb_state = S_TLB_FINISH;
            end




            S_TLB_CLEAR_START: begin
                tlb_mem_en = 1'b1;
                rin_tlb_mem_addr = r_tlb_mem_addr + 1;
                next_tlb_state = S_TLB_CLEAR_CHECK;
            end


            S_TLB_CLEAR_CHECK: begin

                if (r_tlb_valid_entry[r_tlb_mem_addr]) begin
                    tlb_mem_en = 1'b1;
                end


                if (r2_tlb_valid_entry_bit && !tlb_rdata_perm[TCU_TLB_PERM_SIZE-1]) begin
                    rin_tlb_mem_addr = r_tlb_mem_addr - 1;
                    next_tlb_state = S_TLB_CLEAR_REMOVE;
                end


                else if ((r_tlb_mem_addr >= first_valid_entry) && (r_tlb_mem_addr < last_valid_entry)) begin
                    rin_tlb_mem_addr = r_tlb_mem_addr + 1;
                end


                else begin
                    next_tlb_state = S_TLB_FINISH;
                end
            end

            S_TLB_CLEAR_REMOVE: begin

                rin_tlb_mem_addr = r_tlb_mem_addr + 1;

                rin_tlb_valid_entry[r_tlb_mem_addr] = 1'b0;
                next_tlb_state = S_TLB_CLEAR_START;
            end


            S_TLB_FINISH: begin
                next_tlb_state = S_TLB_IDLE;
            end


            default: next_tlb_state = tlb_state;
        endcase
    end


    assign tlb_rdata_o = r_tlb_mem_rdata;
    assign tlb_active_o = (tlb_state != S_TLB_IDLE);
    assign tlb_done_o = (tlb_state == S_TLB_FINISH);
    assign tlb_error_o = r_tlb_error;


    mem_sp_wrap #(
        .MEM_TYPE("distributed"),
        .MEM_DATAWIDTH(TCU_TLB_DATA_SIZE),
        .MEM_ADDRWIDTH(TLB_ADDR_WIDTH)
    ) tlb_mem (
        .clk    (clk_i),
        .reset  (~reset_n_i),
        .en     (tlb_mem_en),
        .we     ({((TCU_TLB_DATA_SIZE+7)/8){tlb_mem_we}}),
        .addr   (r_tlb_mem_addr),
        .din    (tlb_wdata_i),
        .dout   (tlb_mem_rdata)
    );



endmodule