| module ibex_load_store_unit ( |
| clk_i, |
| rst_ni, |
| data_req_o, |
| data_gnt_i, |
| data_rvalid_i, |
| data_err_i, |
| data_pmp_err_i, |
| data_addr_o, |
| data_we_o, |
| data_be_o, |
| data_wdata_o, |
| data_rdata_i, |
| lsu_we_i, |
| lsu_type_i, |
| lsu_wdata_i, |
| lsu_sign_ext_i, |
| lsu_rdata_o, |
| lsu_rdata_valid_o, |
| lsu_req_i, |
| adder_result_ex_i, |
| addr_incr_req_o, |
| addr_last_o, |
| lsu_req_done_o, |
| lsu_resp_valid_o, |
| load_err_o, |
| store_err_o, |
| busy_o, |
| perf_load_o, |
| perf_store_o |
| ); |
| input wire clk_i; |
| input wire rst_ni; |
| output reg data_req_o; |
| input wire data_gnt_i; |
| input wire data_rvalid_i; |
| input wire data_err_i; |
| input wire data_pmp_err_i; |
| output wire [31:0] data_addr_o; |
| output wire data_we_o; |
| output wire [3:0] data_be_o; |
| output wire [31:0] data_wdata_o; |
| input wire [31:0] data_rdata_i; |
| input wire lsu_we_i; |
| input wire [1:0] lsu_type_i; |
| input wire [31:0] lsu_wdata_i; |
| input wire lsu_sign_ext_i; |
| output wire [31:0] lsu_rdata_o; |
| output wire lsu_rdata_valid_o; |
| input wire lsu_req_i; |
| input wire [31:0] adder_result_ex_i; |
| output reg addr_incr_req_o; |
| output wire [31:0] addr_last_o; |
| output wire lsu_req_done_o; |
| output wire lsu_resp_valid_o; |
| output wire load_err_o; |
| output wire store_err_o; |
| output wire busy_o; |
| output reg perf_load_o; |
| output reg perf_store_o; |
| wire [31:0] data_addr; |
| wire [31:0] data_addr_w_aligned; |
| reg [31:0] addr_last_q; |
| wire [31:0] addr_last_d; |
| reg addr_update; |
| reg ctrl_update; |
| reg rdata_update; |
| reg [31:8] rdata_q; |
| reg [1:0] rdata_offset_q; |
| reg [1:0] data_type_q; |
| reg data_sign_ext_q; |
| reg data_we_q; |
| wire [1:0] data_offset; |
| reg [3:0] data_be; |
| reg [31:0] data_wdata; |
| reg [31:0] data_rdata_ext; |
| reg [31:0] rdata_w_ext; |
| reg [31:0] rdata_h_ext; |
| reg [31:0] rdata_b_ext; |
| wire split_misaligned_access; |
| reg handle_misaligned_q; |
| reg handle_misaligned_d; |
| reg pmp_err_q; |
| reg pmp_err_d; |
| reg lsu_err_q; |
| reg lsu_err_d; |
| wire data_or_pmp_err; |
| reg [2:0] ls_fsm_cs; |
| reg [2:0] ls_fsm_ns; |
| assign data_addr = adder_result_ex_i; |
| assign data_offset = data_addr[1:0]; |
| always @(*) |
| case (lsu_type_i) |
| 2'b00: |
| if (!handle_misaligned_q) |
| case (data_offset) |
| 2'b00: data_be = 4'b1111; |
| 2'b01: data_be = 4'b1110; |
| 2'b10: data_be = 4'b1100; |
| 2'b11: data_be = 4'b1000; |
| default: data_be = 4'b1111; |
| endcase |
| else |
| case (data_offset) |
| 2'b00: data_be = 4'b0000; |
| 2'b01: data_be = 4'b0001; |
| 2'b10: data_be = 4'b0011; |
| 2'b11: data_be = 4'b0111; |
| default: data_be = 4'b1111; |
| endcase |
| 2'b01: |
| if (!handle_misaligned_q) |
| case (data_offset) |
| 2'b00: data_be = 4'b0011; |
| 2'b01: data_be = 4'b0110; |
| 2'b10: data_be = 4'b1100; |
| 2'b11: data_be = 4'b1000; |
| default: data_be = 4'b1111; |
| endcase |
| else |
| data_be = 4'b0001; |
| 2'b10, 2'b11: |
| case (data_offset) |
| 2'b00: data_be = 4'b0001; |
| 2'b01: data_be = 4'b0010; |
| 2'b10: data_be = 4'b0100; |
| 2'b11: data_be = 4'b1000; |
| default: data_be = 4'b1111; |
| endcase |
| default: data_be = 4'b1111; |
| endcase |
| always @(*) |
| case (data_offset) |
| 2'b00: data_wdata = lsu_wdata_i[31:0]; |
| 2'b01: data_wdata = {lsu_wdata_i[23:0], lsu_wdata_i[31:24]}; |
| 2'b10: data_wdata = {lsu_wdata_i[15:0], lsu_wdata_i[31:16]}; |
| 2'b11: data_wdata = {lsu_wdata_i[7:0], lsu_wdata_i[31:8]}; |
| default: data_wdata = lsu_wdata_i[31:0]; |
| endcase |
| always @(posedge clk_i or negedge rst_ni) |
| if (!rst_ni) |
| rdata_q <= 1'sb0; |
| else if (rdata_update) |
| rdata_q <= data_rdata_i[31:8]; |
| always @(posedge clk_i or negedge rst_ni) |
| if (!rst_ni) begin |
| rdata_offset_q <= 2'h0; |
| data_type_q <= 2'h0; |
| data_sign_ext_q <= 1'b0; |
| data_we_q <= 1'b0; |
| end |
| else if (ctrl_update) begin |
| rdata_offset_q <= data_offset; |
| data_type_q <= lsu_type_i; |
| data_sign_ext_q <= lsu_sign_ext_i; |
| data_we_q <= lsu_we_i; |
| end |
| assign addr_last_d = (addr_incr_req_o ? data_addr_w_aligned : data_addr); |
| always @(posedge clk_i or negedge rst_ni) |
| if (!rst_ni) |
| addr_last_q <= 1'sb0; |
| else if (addr_update) |
| addr_last_q <= addr_last_d; |
| always @(*) |
| case (rdata_offset_q) |
| 2'b00: rdata_w_ext = data_rdata_i[31:0]; |
| 2'b01: rdata_w_ext = {data_rdata_i[7:0], rdata_q[31:8]}; |
| 2'b10: rdata_w_ext = {data_rdata_i[15:0], rdata_q[31:16]}; |
| 2'b11: rdata_w_ext = {data_rdata_i[23:0], rdata_q[31:24]}; |
| default: rdata_w_ext = data_rdata_i[31:0]; |
| endcase |
| always @(*) |
| case (rdata_offset_q) |
| 2'b00: |
| if (!data_sign_ext_q) |
| rdata_h_ext = {16'h0000, data_rdata_i[15:0]}; |
| else |
| rdata_h_ext = {{16 {data_rdata_i[15]}}, data_rdata_i[15:0]}; |
| 2'b01: |
| if (!data_sign_ext_q) |
| rdata_h_ext = {16'h0000, data_rdata_i[23:8]}; |
| else |
| rdata_h_ext = {{16 {data_rdata_i[23]}}, data_rdata_i[23:8]}; |
| 2'b10: |
| if (!data_sign_ext_q) |
| rdata_h_ext = {16'h0000, data_rdata_i[31:16]}; |
| else |
| rdata_h_ext = {{16 {data_rdata_i[31]}}, data_rdata_i[31:16]}; |
| 2'b11: |
| if (!data_sign_ext_q) |
| rdata_h_ext = {16'h0000, data_rdata_i[7:0], rdata_q[31:24]}; |
| else |
| rdata_h_ext = {{16 {data_rdata_i[7]}}, data_rdata_i[7:0], rdata_q[31:24]}; |
| default: rdata_h_ext = {16'h0000, data_rdata_i[15:0]}; |
| endcase |
| always @(*) |
| case (rdata_offset_q) |
| 2'b00: |
| if (!data_sign_ext_q) |
| rdata_b_ext = {24'h000000, data_rdata_i[7:0]}; |
| else |
| rdata_b_ext = {{24 {data_rdata_i[7]}}, data_rdata_i[7:0]}; |
| 2'b01: |
| if (!data_sign_ext_q) |
| rdata_b_ext = {24'h000000, data_rdata_i[15:8]}; |
| else |
| rdata_b_ext = {{24 {data_rdata_i[15]}}, data_rdata_i[15:8]}; |
| 2'b10: |
| if (!data_sign_ext_q) |
| rdata_b_ext = {24'h000000, data_rdata_i[23:16]}; |
| else |
| rdata_b_ext = {{24 {data_rdata_i[23]}}, data_rdata_i[23:16]}; |
| 2'b11: |
| if (!data_sign_ext_q) |
| rdata_b_ext = {24'h000000, data_rdata_i[31:24]}; |
| else |
| rdata_b_ext = {{24 {data_rdata_i[31]}}, data_rdata_i[31:24]}; |
| default: rdata_b_ext = {24'h000000, data_rdata_i[7:0]}; |
| endcase |
| always @(*) |
| case (data_type_q) |
| 2'b00: data_rdata_ext = rdata_w_ext; |
| 2'b01: data_rdata_ext = rdata_h_ext; |
| 2'b10, 2'b11: data_rdata_ext = rdata_b_ext; |
| default: data_rdata_ext = rdata_w_ext; |
| endcase |
| assign split_misaligned_access = ((lsu_type_i == 2'b00) && (data_offset != 2'b00)) || ((lsu_type_i == 2'b01) && (data_offset == 2'b11)); |
| always @(*) begin |
| ls_fsm_ns = ls_fsm_cs; |
| data_req_o = 1'b0; |
| addr_incr_req_o = 1'b0; |
| handle_misaligned_d = handle_misaligned_q; |
| pmp_err_d = pmp_err_q; |
| lsu_err_d = lsu_err_q; |
| addr_update = 1'b0; |
| ctrl_update = 1'b0; |
| rdata_update = 1'b0; |
| perf_load_o = 1'b0; |
| perf_store_o = 1'b0; |
| case (ls_fsm_cs) |
| 3'd0: begin |
| pmp_err_d = 1'b0; |
| if (lsu_req_i) begin |
| data_req_o = 1'b1; |
| pmp_err_d = data_pmp_err_i; |
| lsu_err_d = 1'b0; |
| perf_load_o = ~lsu_we_i; |
| perf_store_o = lsu_we_i; |
| if (data_gnt_i) begin |
| ctrl_update = 1'b1; |
| addr_update = 1'b1; |
| handle_misaligned_d = split_misaligned_access; |
| ls_fsm_ns = (split_misaligned_access ? 3'd2 : 3'd0); |
| end |
| else |
| ls_fsm_ns = (split_misaligned_access ? 3'd1 : 3'd3); |
| end |
| end |
| 3'd1: begin |
| data_req_o = 1'b1; |
| if (data_gnt_i || pmp_err_q) begin |
| addr_update = 1'b1; |
| ctrl_update = 1'b1; |
| handle_misaligned_d = 1'b1; |
| ls_fsm_ns = 3'd2; |
| end |
| end |
| 3'd2: begin |
| data_req_o = 1'b1; |
| addr_incr_req_o = 1'b1; |
| if (data_rvalid_i || pmp_err_q) begin |
| pmp_err_d = data_pmp_err_i; |
| lsu_err_d = data_err_i | pmp_err_q; |
| rdata_update = ~data_we_q; |
| ls_fsm_ns = (data_gnt_i ? 3'd0 : 3'd3); |
| addr_update = data_gnt_i & ~(data_err_i | pmp_err_q); |
| handle_misaligned_d = ~data_gnt_i; |
| end |
| else if (data_gnt_i) begin |
| ls_fsm_ns = 3'd4; |
| handle_misaligned_d = 1'b0; |
| end |
| end |
| 3'd3: begin |
| addr_incr_req_o = handle_misaligned_q; |
| data_req_o = 1'b1; |
| if (data_gnt_i || pmp_err_q) begin |
| ctrl_update = 1'b1; |
| addr_update = ~lsu_err_q; |
| ls_fsm_ns = 3'd0; |
| handle_misaligned_d = 1'b0; |
| end |
| end |
| 3'd4: begin |
| addr_incr_req_o = 1'b1; |
| if (data_rvalid_i) begin |
| pmp_err_d = data_pmp_err_i; |
| lsu_err_d = data_err_i; |
| addr_update = ~data_err_i; |
| rdata_update = ~data_we_q; |
| ls_fsm_ns = 3'd0; |
| end |
| end |
| default: ls_fsm_ns = 3'd0; |
| endcase |
| end |
| assign lsu_req_done_o = (lsu_req_i | (ls_fsm_cs != 3'd0)) & (ls_fsm_ns == 3'd0); |
| always @(posedge clk_i or negedge rst_ni) |
| if (!rst_ni) begin |
| ls_fsm_cs <= 3'd0; |
| handle_misaligned_q <= 1'sb0; |
| pmp_err_q <= 1'sb0; |
| lsu_err_q <= 1'sb0; |
| end |
| else begin |
| ls_fsm_cs <= ls_fsm_ns; |
| handle_misaligned_q <= handle_misaligned_d; |
| pmp_err_q <= pmp_err_d; |
| lsu_err_q <= lsu_err_d; |
| end |
| assign data_or_pmp_err = (lsu_err_q | data_err_i) | pmp_err_q; |
| assign lsu_resp_valid_o = (data_rvalid_i | pmp_err_q) & (ls_fsm_cs == 3'd0); |
| assign lsu_rdata_valid_o = (((ls_fsm_cs == 3'd0) & data_rvalid_i) & ~data_or_pmp_err) & ~data_we_q; |
| assign lsu_rdata_o = data_rdata_ext; |
| assign data_addr_w_aligned = {data_addr[31:2], 2'b00}; |
| assign data_addr_o = data_addr_w_aligned; |
| assign data_wdata_o = data_wdata; |
| assign data_we_o = lsu_we_i; |
| assign data_be_o = data_be; |
| assign addr_last_o = addr_last_q; |
| assign load_err_o = (data_or_pmp_err & ~data_we_q) & lsu_resp_valid_o; |
| assign store_err_o = (data_or_pmp_err & data_we_q) & lsu_resp_valid_o; |
| assign busy_o = ls_fsm_cs != 3'd0; |
| endmodule |