ibex/rtl/ibex_load_store_unit.sv
Alex Bradbury 27e68bd76e Convert from Solderpad to standard Apache 2.0 license
This change has been informed by advice from the lowRISC legal
committee.

The Solderpad 0.51 license states "the Licensor permits any Work
licensed under this License, at the option of the Licensee, to be
treated as licensed under the Apache License Version 2.0". We use this
freedom to convert license markings to Apache 2.0. This commit ensures
that we retain all authorship and copyright attribution information.
2019-04-26 15:05:17 +01:00

494 lines
16 KiB
Systemverilog

// Copyright lowRISC contributors.
// Copyright 2018 ETH Zurich and University of Bologna.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
////////////////////////////////////////////////////////////////////////////////
// Engineer: Igor Loi - igor.loi@unibo.it //
// //
// Additional contributions by: //
// Andreas Traber - atraber@iis.ee.ethz.ch //
// Markus Wegmann - markus.wegmann@technokrat.ch //
// Davide Schiavone - pschiavo@iis.ee.ethz.ch //
// //
// Design Name: Load Store Unit //
// Project Name: ibex //
// Language: SystemVerilog //
// //
// Description: Load Store Unit, used to eliminate multiple access during //
// processor stalls, and to align bytes and halfwords //
// //
////////////////////////////////////////////////////////////////////////////////
`include "ibex_config.sv"
module ibex_load_store_unit
(
input logic clk,
input logic rst_n,
// output to data memory
output logic data_req_o,
input logic data_gnt_i,
input logic data_rvalid_i,
input logic data_err_i,
output logic [31:0] data_addr_o,
output logic data_we_o,
output logic [3:0] data_be_o,
output logic [31:0] data_wdata_o,
input logic [31:0] data_rdata_i,
// signals from ex stage
input logic data_we_ex_i, // write enable -> from ex stage
input logic [1:0] data_type_ex_i, // Data type word, halfword, byte -> from ex stage
input logic [31:0] data_wdata_ex_i, // data to write to memory -> from ex stage
input logic [1:0] data_reg_offset_ex_i, // offset inside register for stores -> from ex stage
input logic data_sign_ext_ex_i, // sign extension -> from ex stage
output logic [31:0] data_rdata_ex_o, // requested data -> to ex stage
input logic data_req_ex_i, // data request -> from ex stage
input logic [31:0] adder_result_ex_i,
output logic data_misaligned_o, // misaligned access was detected -> to controller
output logic [31:0] misaligned_addr_o,
// exception signals
output logic load_err_o,
output logic store_err_o,
// stall signal
output logic lsu_update_addr_o, // LSU ready for new data in EX stage
output logic data_valid_o,
output logic busy_o
);
logic [31:0] data_addr_int;
// registers for data_rdata alignment and sign extension
logic [1:0] data_type_q;
logic [1:0] rdata_offset_q;
logic data_sign_ext_q;
logic data_we_q;
logic [1:0] wdata_offset; // mux control for data to be written to memory
logic [3:0] data_be;
logic [31:0] data_wdata;
logic misaligned_st; // high if we are currently performing the second part of a misaligned store
logic data_misaligned, data_misaligned_q;
logic increase_address;
enum logic [2:0] { IDLE, WAIT_GNT_MIS, WAIT_RVALID_MIS, WAIT_GNT, WAIT_RVALID } CS, NS;
logic [31:0] rdata_q;
///////////////////////////////// BE generation ////////////////////////////////
always_comb
begin
case (data_type_ex_i) // Data type 00 Word, 01 Half word, 11,10 byte
2'b00:
begin // Writing a word
if (misaligned_st == 1'b0)
begin // non-misaligned case
case (data_addr_int[1:0])
2'b00: data_be = 4'b1111;
2'b01: data_be = 4'b1110;
2'b10: data_be = 4'b1100;
2'b11: data_be = 4'b1000;
endcase; // case (data_addr_int[1:0])
end
else
begin // misaligned case
case (data_addr_int[1:0])
2'b00: data_be = 4'b0000; // this is not used, but included for completeness
2'b01: data_be = 4'b0001;
2'b10: data_be = 4'b0011;
2'b11: data_be = 4'b0111;
endcase; // case (data_addr_int[1:0])
end
end
2'b01:
begin // Writing a half word
if (misaligned_st == 1'b0)
begin // non-misaligned case
case (data_addr_int[1:0])
2'b00: data_be = 4'b0011;
2'b01: data_be = 4'b0110;
2'b10: data_be = 4'b1100;
2'b11: data_be = 4'b1000;
endcase; // case (data_addr_int[1:0])
end
else
begin // misaligned case
data_be = 4'b0001;
end
end
2'b10,
2'b11: begin // Writing a byte
case (data_addr_int[1:0])
2'b00: data_be = 4'b0001;
2'b01: data_be = 4'b0010;
2'b10: data_be = 4'b0100;
2'b11: data_be = 4'b1000;
endcase; // case (data_addr_int[1:0])
end
endcase; // case (data_type_ex_i)
end
// prepare data to be written to the memory
// we handle misaligned accesses, half word and byte accesses and
// register offsets here
assign wdata_offset = data_addr_int[1:0] - data_reg_offset_ex_i[1:0];
always_comb
begin
case (wdata_offset)
2'b00: data_wdata = data_wdata_ex_i[31:0];
2'b01: data_wdata = {data_wdata_ex_i[23:0], data_wdata_ex_i[31:24]};
2'b10: data_wdata = {data_wdata_ex_i[15:0], data_wdata_ex_i[31:16]};
2'b11: data_wdata = {data_wdata_ex_i[ 7:0], data_wdata_ex_i[31: 8]};
endcase; // case (wdata_offset)
end
// FF for rdata alignment and sign-extension
always_ff @(posedge clk, negedge rst_n)
begin
if(rst_n == 1'b0)
begin
data_type_q <= '0;
rdata_offset_q <= '0;
data_sign_ext_q <= '0;
data_we_q <= 1'b0;
end
else if (data_gnt_i == 1'b1) // request was granted, we wait for rvalid and can continue to WB
begin
data_type_q <= data_type_ex_i;
rdata_offset_q <= data_addr_int[1:0];
data_sign_ext_q <= data_sign_ext_ex_i;
data_we_q <= data_we_ex_i;
end
end
////////////////////////////////////////////////////////////////////////
// ____ _ _____ _ _ //
// / ___|(_) __ _ _ __ | ____|_ _| |_ ___ _ __ ___(_) ___ _ __ //
// \___ \| |/ _` | '_ \ | _| \ \/ / __/ _ \ '_ \/ __| |/ _ \| '_ \ //
// ___) | | (_| | | | | | |___ > <| || __/ | | \__ \ | (_) | | | | //
// |____/|_|\__, |_| |_| |_____/_/\_\\__\___|_| |_|___/_|\___/|_| |_| //
// |___/ //
////////////////////////////////////////////////////////////////////////
logic [31:0] data_rdata_ext;
logic [31:0] rdata_w_ext; // sign extension for words, actually only misaligned assembly
logic [31:0] rdata_h_ext; // sign extension for half words
logic [31:0] rdata_b_ext; // sign extension for bytes
// take care of misaligned words
always_comb
begin
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]};
endcase
end
// sign extension for half words
always_comb
begin
case (rdata_offset_q)
2'b00:
begin
if (data_sign_ext_q == 1'b0)
rdata_h_ext = {16'h0000, data_rdata_i[15:0]};
else
rdata_h_ext = {{16{data_rdata_i[15]}}, data_rdata_i[15:0]};
end
2'b01:
begin
if (data_sign_ext_q == 1'b0)
rdata_h_ext = {16'h0000, data_rdata_i[23:8]};
else
rdata_h_ext = {{16{data_rdata_i[23]}}, data_rdata_i[23:8]};
end
2'b10:
begin
if (data_sign_ext_q == 1'b0)
rdata_h_ext = {16'h0000, data_rdata_i[31:16]};
else
rdata_h_ext = {{16{data_rdata_i[31]}}, data_rdata_i[31:16]};
end
2'b11:
begin
if (data_sign_ext_q == 1'b0)
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]};
end
endcase // case (rdata_offset_q)
end
// sign extension for bytes
always_comb
begin
case (rdata_offset_q)
2'b00:
begin
if (data_sign_ext_q == 1'b0)
rdata_b_ext = {24'h00_0000, data_rdata_i[7:0]};
else
rdata_b_ext = {{24{data_rdata_i[7]}}, data_rdata_i[7:0]};
end
2'b01: begin
if (data_sign_ext_q == 1'b0)
rdata_b_ext = {24'h00_0000, data_rdata_i[15:8]};
else
rdata_b_ext = {{24{data_rdata_i[15]}}, data_rdata_i[15:8]};
end
2'b10:
begin
if (data_sign_ext_q == 1'b0)
rdata_b_ext = {24'h00_0000, data_rdata_i[23:16]};
else
rdata_b_ext = {{24{data_rdata_i[23]}}, data_rdata_i[23:16]};
end
2'b11:
begin
if (data_sign_ext_q == 1'b0)
rdata_b_ext = {24'h00_0000, data_rdata_i[31:24]};
else
rdata_b_ext = {{24{data_rdata_i[31]}}, data_rdata_i[31:24]};
end
endcase // case (rdata_offset_q)
end
// select word, half word or byte sign extended version
always_comb
begin
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;
endcase //~case(rdata_type_q)
end
always_ff @(posedge clk, negedge rst_n)
begin
if(rst_n == 1'b0)
begin
CS <= IDLE;
rdata_q <= '0;
data_misaligned_q <= '0;
misaligned_addr_o <= 32'b0;
end
else
begin
CS <= NS;
if (lsu_update_addr_o) begin
data_misaligned_q <= data_misaligned;
if(increase_address) begin
misaligned_addr_o <= data_addr_int;
end
end
if (data_rvalid_i && (~data_we_q))
begin
// if we have detected a misaligned access, and we are
// currently doing the first part of this access, then
// store the data coming from memory in rdata_q.
// In all other cases, rdata_q gets the value that we are
// writing to the register file
if ((data_misaligned_q == 1'b1) || (data_misaligned == 1'b1))
rdata_q <= data_rdata_i;
else
rdata_q <= data_rdata_ext;
end
end
end
// output to register file
assign data_rdata_ex_o = (data_rvalid_i == 1'b1) ? data_rdata_ext : rdata_q;
// output to data interface
assign data_addr_o = data_addr_int;
assign data_wdata_o = data_wdata;
assign data_we_o = data_we_ex_i;
assign data_be_o = data_be;
assign misaligned_st = data_misaligned_q;
assign load_err_o = 1'b0;
assign store_err_o = 1'b0;
// FSM
always_comb
begin
NS = CS;
data_req_o = 1'b0;
lsu_update_addr_o = 1'b0;
data_valid_o = 1'b0;
increase_address = 1'b0;
data_misaligned_o = 1'b0;
case(CS)
// starts from not active and stays in IDLE until request was granted
IDLE:
begin
if (data_req_ex_i) begin
data_req_o = data_req_ex_i;
if(data_gnt_i) begin
lsu_update_addr_o = 1'b1;
increase_address = data_misaligned;
NS = data_misaligned ? WAIT_RVALID_MIS : WAIT_RVALID;
end
else begin
NS = data_misaligned ? WAIT_GNT_MIS : WAIT_GNT;
end
end
end //~ IDLE
WAIT_GNT_MIS:
begin
data_req_o = 1'b1;
if(data_gnt_i) begin
lsu_update_addr_o = 1'b1;
increase_address = data_misaligned;
NS = WAIT_RVALID_MIS;
end
end //~ WAIT_GNT_MIS
// wait for rvalid in WB stage and send a new request if there is any
WAIT_RVALID_MIS:
begin
//increase_address goes down, we already have the proper address
increase_address = 1'b0;
//tell the controller to update the address
data_misaligned_o = 1'b1;
data_req_o = 1'b0;
lsu_update_addr_o = data_gnt_i;
if(data_rvalid_i) begin
//if first part rvalid is received
data_req_o = 1'b1;
if(data_gnt_i) begin
//second grant is received
NS = WAIT_RVALID;
//in this stage we already received the first valid but no the second one
//it differes from WAIT_RVALID_MIS because we do not send other requests
end
else begin
//second grant is NOT received, but first rvalid yes
//lsu_update_addr_o is 0 so data_misaligned_q stays high in WAIT_GNT
//increase address stays the same as well
NS = WAIT_GNT; // [1]
end
end
else begin
//if first part rvalid is NOT received
//the second grand is not received either by protocol.
//stay here
NS = WAIT_RVALID_MIS;
end
end
WAIT_GNT:
begin
data_misaligned_o = data_misaligned_q;
//useful in case [1]
data_req_o = 1'b1;
if(data_gnt_i) begin
lsu_update_addr_o = 1'b1;
NS = WAIT_RVALID;
end
end //~ WAIT_GNT
WAIT_RVALID:
begin
data_req_o = 1'b0;
if(data_rvalid_i) begin
data_valid_o = 1'b1;
NS = IDLE;
end
else begin
NS = WAIT_RVALID;
end
end //~ WAIT_RVALID
default: begin
NS = IDLE;
end
endcase
end
// check for misaligned accesses that need a second memory access
// If one is detected, this is signaled with data_misaligned_o to
// the controller which selectively stalls the pipeline
always_comb
begin
data_misaligned = 1'b0;
if((data_req_ex_i == 1'b1) && (data_misaligned_q == 1'b0))
begin
case (data_type_ex_i)
2'b00: // word
begin
if(data_addr_int[1:0] != 2'b00)
data_misaligned = 1'b1;
end
2'b01: // half word
begin
if(data_addr_int[1:0] == 2'b11)
data_misaligned = 1'b1;
end
default: ;
endcase // case (data_type_ex_i)
end
end
assign data_addr_int = adder_result_ex_i;
assign busy_o = (CS == WAIT_RVALID) || (data_req_o == 1'b1);
//////////////////////////////////////////////////////////////////////////////
// Assertions
//////////////////////////////////////////////////////////////////////////////
`ifndef VERILATOR
// make sure there is no new request when the old one is not yet completely done
// i.e. it should not be possible to get a grant without an rvalid for the
// last request
assert property (
@(posedge clk) ((CS == WAIT_RVALID) && (data_gnt_i == 1'b1)) |-> (data_rvalid_i == 1'b1) );
// there should be no rvalid when we are in IDLE
assert property (
@(posedge clk) (CS == IDLE) |-> (data_rvalid_i == 1'b0) );
// assert that errors are only sent at the same time as grant
assert property ( @(posedge clk) (data_err_i) |-> (data_gnt_i) );
// assert that the address does not contain X when request is sent
assert property ( @(posedge clk) (data_req_o) |-> (!$isunknown(data_addr_o)) );
`endif
endmodule