`include "csr.h"

module addr_trans
#(
    parameter TLBNUM = 32
)
(
    input                  clk                  ,
    input  [ 9:0]          asid                 ,
    //trans mode
    input                  inst_addr_trans_en   ,
    input                  data_addr_trans_en   ,
    //inst addr trans
    input                  inst_fetch           ,
    input  [31:0]          inst_vaddr           ,
    input                  inst_dmw0_en         ,
    input                  inst_dmw1_en         ,
    output [ 7:0]          inst_index           ,
    output [19:0]          inst_tag             ,
    output [ 3:0]          inst_offset          ,
    output                 inst_tlb_found       ,
    output                 inst_tlb_v           ,
    output                 inst_tlb_d           ,
    output [ 1:0]          inst_tlb_mat         ,
    output [ 1:0]          inst_tlb_plv         ,
    //data addr trans
    input                  data_fetch           ,
    input  [31:0]          data_vaddr           ,
    input                  data_dmw0_en         ,
    input                  data_dmw1_en         ,
    input                  cacop_op_mode_di     ,
    output [ 7:0]          data_index           ,
    output [19:0]          data_tag             ,
    output [ 3:0]          data_offset          ,
    output                 data_tlb_found       ,
    output [ 4:0]          data_tlb_index       ,
    output                 data_tlb_v           ,
    output                 data_tlb_d           ,
    output [ 1:0]          data_tlb_mat         ,
    output [ 1:0]          data_tlb_plv         ,
    //tlbwi tlbwr tlb write
    input                  tlbfill_en           ,
    input                  tlbwr_en             ,
    input  [ 4:0]          rand_index           ,
    input  [31:0]          tlbehi_in            ,
    input  [31:0]          tlbelo0_in           ,
    input  [31:0]          tlbelo1_in           ,
    input  [31:0]          tlbidx_in            , 
    input  [ 5:0]          ecode_in             ,
    //tlbr tlb read
    output [31:0]          tlbehi_out           ,
    output [31:0]          tlbelo0_out          ,
    output [31:0]          tlbelo1_out          ,
    output [31:0]          tlbidx_out           ,
    output [ 9:0]          asid_out             ,
    //invtlb 
    input                  invtlb_en            ,
    input  [ 9:0]          invtlb_asid          ,
    input  [18:0]          invtlb_vpn           ,
    input  [ 4:0]          invtlb_op            ,
    //from csr
    input  [31:0]          csr_dmw0             ,
    input  [31:0]          csr_dmw1             ,
    input                  csr_da               ,
    input                  csr_pg               
);

wire [18:0] s0_vppn     ;
wire        s0_odd_page ;
wire [ 5:0] s0_ps       ;
wire [19:0] s0_ppn      ;

wire [18:0] s1_vppn     ;
wire        s1_odd_page ;
wire [ 5:0] s1_ps       ;
wire [19:0] s1_ppn      ;

wire        we          ;
wire [ 4:0] w_index     ;
wire [18:0] w_vppn      ;
wire        w_g         ;
wire [ 5:0] w_ps        ;
wire        w_e         ;
wire        w_v0        ;
wire        w_d0        ;
wire [ 1:0] w_mat0      ;
wire [ 1:0] w_plv0      ;
wire [19:0] w_ppn0      ;
wire        w_v1        ;
wire        w_d1        ;
wire [ 1:0] w_mat1      ;
wire [ 1:0] w_plv1      ;
wire [19:0] w_ppn1      ;

wire [ 4:0] r_index     ;
wire [18:0] r_vppn      ;
wire [ 9:0] r_asid      ;
wire        r_g         ;
wire [ 5:0] r_ps        ;
wire        r_e         ;
wire        r_v0        ;
wire        r_d0        ; 
wire [ 1:0] r_mat0      ;
wire [ 1:0] r_plv0      ;
wire [19:0] r_ppn0      ;
wire        r_v1        ;
wire        r_d1        ;
wire [ 1:0] r_mat1      ;
wire [ 1:0] r_plv1      ;
wire [19:0] r_ppn1      ;

reg  [31:0] inst_vaddr_buffer  ;
reg  [31:0] data_vaddr_buffer  ;
wire [31:0] inst_paddr;
wire [31:0] data_paddr;

wire        pg_mode;
wire        da_mode;

always @(posedge clk) begin
    if (inst_fetch) begin
        inst_vaddr_buffer <= inst_vaddr;
    end

    if (data_fetch) begin
        data_vaddr_buffer <= data_vaddr;
    end
end

//trans search port sig
assign s0_vppn     = inst_vaddr[31:13];
assign s0_odd_page = inst_vaddr[12];

assign s1_vppn     = data_vaddr[31:13];
assign s1_odd_page = data_vaddr[12];

//trans write port sig
assign we      = tlbfill_en || tlbwr_en;
assign w_index = ({5{tlbfill_en}} & rand_index) | ({5{tlbwr_en}} & tlbidx_in[`INDEX]);
assign w_vppn  = tlbehi_in[`VPPN];
assign w_g     = tlbelo0_in[`TLB_G] && tlbelo1_in[`TLB_G];
assign w_ps    = tlbidx_in[`PS];
assign w_e     = (ecode_in == 6'h3f) ? 1'b1 : !tlbidx_in[`NE];
assign w_v0    = tlbelo0_in[`TLB_V];
assign w_d0    = tlbelo0_in[`TLB_D];
assign w_plv0  = tlbelo0_in[`TLB_PLV];
assign w_mat0  = tlbelo0_in[`TLB_MAT];
assign w_ppn0  = tlbelo0_in[`TLB_PPN_EN];
assign w_v1    = tlbelo1_in[`TLB_V];
assign w_d1    = tlbelo1_in[`TLB_D];
assign w_plv1  = tlbelo1_in[`TLB_PLV];
assign w_mat1  = tlbelo1_in[`TLB_MAT];
assign w_ppn1  = tlbelo1_in[`TLB_PPN_EN];

//trans read port sig
assign r_index      = tlbidx_in[`INDEX];
assign tlbehi_out   = {r_vppn, 13'b0};
assign tlbelo0_out  = {4'b0, r_ppn0, 1'b0, r_g, r_mat0, r_plv0, r_d0, r_v0};
assign tlbelo1_out  = {4'b0, r_ppn1, 1'b0, r_g, r_mat1, r_plv1, r_d1, r_v1};
assign tlbidx_out   = {!r_e, 1'b0, r_ps, 24'b0}; //note do not write index
assign asid_out     = r_asid;

tlb_entry tlb_entry(
    .clk            (clk            ),
    // search port 0
    .s0_fetch       (inst_fetch     ),
    .s0_vppn        (s0_vppn        ),
    .s0_odd_page    (s0_odd_page    ),
    .s0_asid        (asid           ),
    .s0_found       (inst_tlb_found ),
    .s0_index       (),
    .s0_ps          (s0_ps          ),
    .s0_ppn         (s0_ppn         ),
    .s0_v           (inst_tlb_v     ),
    .s0_d           (inst_tlb_d     ),
    .s0_mat         (inst_tlb_mat   ),
    .s0_plv         (inst_tlb_plv   ),
    // search port 1
    .s1_fetch       (data_fetch     ),
    .s1_vppn        (s1_vppn        ),
    .s1_odd_page    (s1_odd_page    ),
    .s1_asid        (asid           ),
    .s1_found       (data_tlb_found ),
    .s1_index       (data_tlb_index ),
    .s1_ps          (s1_ps          ),
    .s1_ppn         (s1_ppn         ),
    .s1_v           (data_tlb_v     ),
    .s1_d           (data_tlb_d     ),
    .s1_mat         (data_tlb_mat   ),
    .s1_plv         (data_tlb_plv   ),
    // write port 
    .we             (we             ),     
    .w_index        (w_index        ),
    .w_vppn         (w_vppn         ),
    .w_asid         (asid           ),
    .w_g            (w_g            ),
    .w_ps           (w_ps           ),
    .w_e            (w_e            ),
    .w_v0           (w_v0           ),
    .w_d0           (w_d0           ),
    .w_plv0         (w_plv0         ),
    .w_mat0         (w_mat0         ),
    .w_ppn0         (w_ppn0         ),
    .w_v1           (w_v1           ),
    .w_d1           (w_d1           ),
    .w_plv1         (w_plv1         ),
    .w_mat1         (w_mat1         ),
    .w_ppn1         (w_ppn1         ),
    //read port 
    .r_index        (r_index        ),
    .r_vppn         (r_vppn         ),
    .r_asid         (r_asid         ),
    .r_g            (r_g            ),
    .r_ps           (r_ps           ),
    .r_e            (r_e            ),
    .r_v0           (r_v0           ),
    .r_d0           (r_d0           ),
    .r_mat0         (r_mat0         ),
    .r_plv0         (r_plv0         ),
    .r_ppn0         (r_ppn0         ),
    .r_v1           (r_v1           ),
    .r_d1           (r_d1           ),
    .r_mat1         (r_mat1         ),
    .r_plv1         (r_plv1         ),
    .r_ppn1         (r_ppn1         ),
    //invalid port
    .inv_en         (invtlb_en      ),
    .inv_op         (invtlb_op      ),
    .inv_asid       (invtlb_asid    ),
    .inv_vpn        (invtlb_vpn     )
);

assign pg_mode = !csr_da &&  csr_pg;
assign da_mode =  csr_da && !csr_pg;

assign inst_paddr = (pg_mode && inst_dmw0_en) ? {csr_dmw0[`PSEG], inst_vaddr_buffer[28:0]} :
                    (pg_mode && inst_dmw1_en) ? {csr_dmw1[`PSEG], inst_vaddr_buffer[28:0]} : inst_vaddr_buffer;

assign inst_offset = inst_vaddr[3:0];
assign inst_index  = inst_vaddr[11:4];
assign inst_tag    = inst_addr_trans_en ? ((s0_ps == 6'd12) ? s0_ppn : {s0_ppn[19:10], inst_paddr[21:12]}) : inst_paddr[31:12];

assign data_paddr = (pg_mode && data_dmw0_en && !cacop_op_mode_di) ? {csr_dmw0[`PSEG], data_vaddr_buffer[28:0]} : 
                    (pg_mode && data_dmw1_en && !cacop_op_mode_di) ? {csr_dmw1[`PSEG], data_vaddr_buffer[28:0]} : data_vaddr_buffer;

assign data_offset = data_vaddr[3:0];
assign data_index  = data_vaddr[11:4];
assign data_tag    = data_addr_trans_en ? ((s1_ps == 6'd12) ? s1_ppn : {s1_ppn[19:10], data_paddr[21:12]}) : data_paddr[31:12];

endmodule
