blob: 59b1146b478d965826786f223c1cf752e5762516 [file] [log] [blame]
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;
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 <= {24 {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
always @(posedge clk_i or negedge rst_ni)
if (!rst_ni)
addr_last_q <= {32 {1'sb0}};
else if (addr_update)
addr_last_q <= data_addr;
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));
localparam [2:0] IDLE = 0;
localparam [2:0] WAIT_GNT = 3;
localparam [2:0] WAIT_GNT_MIS = 1;
localparam [2:0] WAIT_RVALID_MIS = 2;
localparam [2:0] WAIT_RVALID_MIS_GNTS_DONE = 4;
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)
IDLE: 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 ? WAIT_RVALID_MIS : IDLE);
end
else
ls_fsm_ns = (split_misaligned_access ? WAIT_GNT_MIS : WAIT_GNT);
end
end
WAIT_GNT_MIS: 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 = WAIT_RVALID_MIS;
end
end
WAIT_RVALID_MIS: 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 ? IDLE : WAIT_GNT);
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 = WAIT_RVALID_MIS_GNTS_DONE;
handle_misaligned_d = 1'b0;
end
end
WAIT_GNT: 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 = IDLE;
handle_misaligned_d = 1'b0;
end
end
WAIT_RVALID_MIS_GNTS_DONE: 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 = IDLE;
end
end
default: ls_fsm_ns = IDLE;
endcase
end
assign lsu_req_done_o = (lsu_req_i | (ls_fsm_cs != IDLE)) & (ls_fsm_ns == IDLE);
always @(posedge clk_i or negedge rst_ni)
if (!rst_ni) begin
ls_fsm_cs <= IDLE;
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 == IDLE);
assign lsu_rdata_valid_o = (((ls_fsm_cs == IDLE) & 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 != IDLE;
endmodule