mirror of
https://github.com/lowRISC/ibex.git
synced 2025-04-24 05:47:36 -04:00
Previously Ibex signalled a major alert on an integrity error (where incoming read data doesn't match its integrity bits) for both read and write responses. This was removed as the data part of a response to a write is ignored. This brings it back in a more measured way. This provides a little extra fault injection hardening as an attacker glitching the memory bus will generate an alert on both read and write responses.
624 lines
22 KiB
Systemverilog
624 lines
22 KiB
Systemverilog
// Copyright lowRISC contributors.
|
|
// Copyright 2018 ETH Zurich and University of Bologna, see also CREDITS.md.
|
|
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
|
/**
|
|
* Load Store Unit
|
|
*
|
|
* Load Store Unit, used to eliminate multiple access during processor stalls,
|
|
* and to align bytes and halfwords.
|
|
*/
|
|
|
|
`include "prim_assert.sv"
|
|
`include "dv_fcov_macros.svh"
|
|
|
|
module ibex_load_store_unit #(
|
|
parameter bit MemECC = 1'b0,
|
|
parameter int unsigned MemDataWidth = MemECC ? 32 + 7 : 32
|
|
) (
|
|
input logic clk_i,
|
|
input logic rst_ni,
|
|
|
|
// data interface
|
|
output logic data_req_o,
|
|
input logic data_gnt_i,
|
|
input logic data_rvalid_i,
|
|
input logic data_bus_err_i,
|
|
input logic data_pmp_err_i,
|
|
|
|
output logic [31:0] data_addr_o,
|
|
output logic data_we_o,
|
|
output logic [3:0] data_be_o,
|
|
output logic [MemDataWidth-1:0] data_wdata_o,
|
|
input logic [MemDataWidth-1:0] data_rdata_i,
|
|
|
|
// signals to/from ID/EX stage
|
|
input logic lsu_we_i, // write enable -> from ID/EX
|
|
input logic [1:0] lsu_type_i, // data type: word, half word, byte -> from ID/EX
|
|
input logic [31:0] lsu_wdata_i, // data to write to memory -> from ID/EX
|
|
input logic lsu_sign_ext_i, // sign extension -> from ID/EX
|
|
|
|
output logic [31:0] lsu_rdata_o, // requested data -> to ID/EX
|
|
output logic lsu_rdata_valid_o,
|
|
input logic lsu_req_i, // data request -> from ID/EX
|
|
|
|
input logic [31:0] adder_result_ex_i, // address computed in ALU -> from ID/EX
|
|
|
|
output logic addr_incr_req_o, // request address increment for
|
|
// misaligned accesses -> to ID/EX
|
|
output logic [31:0] addr_last_o, // address of last transaction -> to controller
|
|
// -> mtval
|
|
// -> AGU for misaligned accesses
|
|
|
|
output logic lsu_req_done_o, // Signals that data request is complete
|
|
// (only need to await final data
|
|
// response) -> to ID/EX
|
|
|
|
output logic lsu_resp_valid_o, // LSU has response from transaction -> to ID/EX
|
|
|
|
// exception signals
|
|
output logic load_err_o,
|
|
output logic load_resp_intg_err_o,
|
|
output logic store_err_o,
|
|
output logic store_resp_intg_err_o,
|
|
|
|
output logic busy_o,
|
|
|
|
output logic perf_load_o,
|
|
output logic perf_store_o
|
|
);
|
|
|
|
logic [31:0] data_addr;
|
|
logic [31:0] data_addr_w_aligned;
|
|
logic [31:0] addr_last_q, addr_last_d;
|
|
|
|
logic addr_update;
|
|
logic ctrl_update;
|
|
logic rdata_update;
|
|
logic [31:8] rdata_q;
|
|
logic [1:0] rdata_offset_q;
|
|
logic [1:0] data_type_q;
|
|
logic data_sign_ext_q;
|
|
logic data_we_q;
|
|
|
|
logic [1:0] data_offset; // mux control for data to be written to memory
|
|
|
|
logic [3:0] data_be;
|
|
logic [31:0] data_wdata;
|
|
|
|
logic [31:0] data_rdata_ext;
|
|
|
|
logic [31:0] rdata_w_ext; // word realignment for misaligned loads
|
|
logic [31:0] rdata_h_ext; // sign extension for half words
|
|
logic [31:0] rdata_b_ext; // sign extension for bytes
|
|
|
|
logic split_misaligned_access;
|
|
logic handle_misaligned_q, handle_misaligned_d; // high after receiving grant for first
|
|
// part of a misaligned access
|
|
logic pmp_err_q, pmp_err_d;
|
|
logic lsu_err_q, lsu_err_d;
|
|
logic data_intg_err, data_or_pmp_err;
|
|
|
|
typedef enum logic [2:0] {
|
|
IDLE, WAIT_GNT_MIS, WAIT_RVALID_MIS, WAIT_GNT,
|
|
WAIT_RVALID_MIS_GNTS_DONE
|
|
} ls_fsm_e;
|
|
|
|
ls_fsm_e ls_fsm_cs, ls_fsm_ns;
|
|
|
|
assign data_addr = adder_result_ex_i;
|
|
assign data_offset = data_addr[1:0];
|
|
|
|
///////////////////
|
|
// BE generation //
|
|
///////////////////
|
|
|
|
always_comb begin
|
|
unique case (lsu_type_i) // Data type 00 Word, 01 Half word, 11,10 byte
|
|
2'b00: begin // Writing a word
|
|
if (!handle_misaligned_q) begin // first part of potentially misaligned transaction
|
|
unique 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 // case (data_offset)
|
|
end else begin // second part of misaligned transaction
|
|
unique case (data_offset)
|
|
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;
|
|
default: data_be = 4'b1111;
|
|
endcase // case (data_offset)
|
|
end
|
|
end
|
|
|
|
2'b01: begin // Writing a half word
|
|
if (!handle_misaligned_q) begin // first part of potentially misaligned transaction
|
|
unique 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 // case (data_offset)
|
|
end else begin // second part of misaligned transaction
|
|
data_be = 4'b0001;
|
|
end
|
|
end
|
|
|
|
2'b10,
|
|
2'b11: begin // Writing a byte
|
|
unique 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 // case (data_offset)
|
|
end
|
|
|
|
default: data_be = 4'b1111;
|
|
endcase // case (lsu_type_i)
|
|
end
|
|
|
|
/////////////////////
|
|
// WData alignment //
|
|
/////////////////////
|
|
|
|
// prepare data to be written to the memory
|
|
// we handle misaligned accesses, half word and byte accesses here
|
|
always_comb begin
|
|
unique 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 // case (data_offset)
|
|
end
|
|
|
|
/////////////////////
|
|
// RData alignment //
|
|
/////////////////////
|
|
|
|
// register for unaligned rdata
|
|
always_ff @(posedge clk_i or negedge rst_ni) begin
|
|
if (!rst_ni) begin
|
|
rdata_q <= '0;
|
|
end else if (rdata_update) begin
|
|
rdata_q <= data_rdata_i[31:8];
|
|
end
|
|
end
|
|
|
|
// registers for transaction control
|
|
always_ff @(posedge clk_i or negedge rst_ni) begin
|
|
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
|
|
end
|
|
|
|
// Store last address for mtval + AGU for misaligned transactions. Do not update in case of
|
|
// errors, mtval needs the (first) failing address. Where an aligned access or the first half of
|
|
// a misaligned access sees an error provide the calculated access address. For the second half of
|
|
// a misaligned access provide the word aligned address of the second half.
|
|
assign addr_last_d = addr_incr_req_o ? data_addr_w_aligned : data_addr;
|
|
|
|
always_ff @(posedge clk_i or negedge rst_ni) begin
|
|
if (!rst_ni) begin
|
|
addr_last_q <= '0;
|
|
end else if (addr_update) begin
|
|
addr_last_q <= addr_last_d;
|
|
end
|
|
end
|
|
|
|
// take care of misaligned words
|
|
always_comb begin
|
|
unique 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
|
|
end
|
|
|
|
////////////////////
|
|
// Sign extension //
|
|
////////////////////
|
|
|
|
// sign extension for half words
|
|
always_comb begin
|
|
unique case (rdata_offset_q)
|
|
2'b00: begin
|
|
if (!data_sign_ext_q) begin
|
|
rdata_h_ext = {16'h0000, data_rdata_i[15:0]};
|
|
end else begin
|
|
rdata_h_ext = {{16{data_rdata_i[15]}}, data_rdata_i[15:0]};
|
|
end
|
|
end
|
|
|
|
2'b01: begin
|
|
if (!data_sign_ext_q) begin
|
|
rdata_h_ext = {16'h0000, data_rdata_i[23:8]};
|
|
end else begin
|
|
rdata_h_ext = {{16{data_rdata_i[23]}}, data_rdata_i[23:8]};
|
|
end
|
|
end
|
|
|
|
2'b10: begin
|
|
if (!data_sign_ext_q) begin
|
|
rdata_h_ext = {16'h0000, data_rdata_i[31:16]};
|
|
end else begin
|
|
rdata_h_ext = {{16{data_rdata_i[31]}}, data_rdata_i[31:16]};
|
|
end
|
|
end
|
|
|
|
2'b11: begin
|
|
if (!data_sign_ext_q) begin
|
|
rdata_h_ext = {16'h0000, data_rdata_i[7:0], rdata_q[31:24]};
|
|
end else begin
|
|
rdata_h_ext = {{16{data_rdata_i[7]}}, data_rdata_i[7:0], rdata_q[31:24]};
|
|
end
|
|
end
|
|
|
|
default: rdata_h_ext = {16'h0000, data_rdata_i[15:0]};
|
|
endcase // case (rdata_offset_q)
|
|
end
|
|
|
|
// sign extension for bytes
|
|
always_comb begin
|
|
unique case (rdata_offset_q)
|
|
2'b00: begin
|
|
if (!data_sign_ext_q) begin
|
|
rdata_b_ext = {24'h00_0000, data_rdata_i[7:0]};
|
|
end else begin
|
|
rdata_b_ext = {{24{data_rdata_i[7]}}, data_rdata_i[7:0]};
|
|
end
|
|
end
|
|
|
|
2'b01: begin
|
|
if (!data_sign_ext_q) begin
|
|
rdata_b_ext = {24'h00_0000, data_rdata_i[15:8]};
|
|
end else begin
|
|
rdata_b_ext = {{24{data_rdata_i[15]}}, data_rdata_i[15:8]};
|
|
end
|
|
end
|
|
|
|
2'b10: begin
|
|
if (!data_sign_ext_q) begin
|
|
rdata_b_ext = {24'h00_0000, data_rdata_i[23:16]};
|
|
end else begin
|
|
rdata_b_ext = {{24{data_rdata_i[23]}}, data_rdata_i[23:16]};
|
|
end
|
|
end
|
|
|
|
2'b11: begin
|
|
if (!data_sign_ext_q) begin
|
|
rdata_b_ext = {24'h00_0000, data_rdata_i[31:24]};
|
|
end else begin
|
|
rdata_b_ext = {{24{data_rdata_i[31]}}, data_rdata_i[31:24]};
|
|
end
|
|
end
|
|
|
|
default: rdata_b_ext = {24'h00_0000, data_rdata_i[7:0]};
|
|
endcase // case (rdata_offset_q)
|
|
end
|
|
|
|
// select word, half word or byte sign extended version
|
|
always_comb begin
|
|
unique 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 // case (data_type_q)
|
|
end
|
|
|
|
///////////////////////////////
|
|
// Read data integrity check //
|
|
///////////////////////////////
|
|
|
|
// SEC_CM: BUS.INTEGRITY
|
|
if (MemECC) begin : g_mem_rdata_ecc
|
|
logic [1:0] ecc_err;
|
|
logic [MemDataWidth-1:0] data_rdata_buf;
|
|
|
|
prim_buf #(.Width(MemDataWidth)) u_prim_buf_instr_rdata (
|
|
.in_i (data_rdata_i),
|
|
.out_o(data_rdata_buf)
|
|
);
|
|
|
|
prim_secded_inv_39_32_dec u_data_intg_dec (
|
|
.data_i (data_rdata_buf),
|
|
.data_o (),
|
|
.syndrome_o (),
|
|
.err_o (ecc_err)
|
|
);
|
|
|
|
// Don't care if error is correctable or not, they're all treated the same
|
|
assign data_intg_err = |ecc_err;
|
|
end else begin : g_no_mem_data_ecc
|
|
assign data_intg_err = 1'b0;
|
|
end
|
|
|
|
/////////////
|
|
// LSU FSM //
|
|
/////////////
|
|
|
|
// check for misaligned accesses that need to be split into two word-aligned accesses
|
|
assign split_misaligned_access =
|
|
((lsu_type_i == 2'b00) && (data_offset != 2'b00)) || // misaligned word access
|
|
((lsu_type_i == 2'b01) && (data_offset == 2'b11)); // misaligned half-word access
|
|
|
|
// FSM
|
|
always_comb 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;
|
|
|
|
unique 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 begin
|
|
ls_fsm_ns = split_misaligned_access ? WAIT_GNT_MIS : WAIT_GNT;
|
|
end
|
|
end
|
|
end
|
|
|
|
WAIT_GNT_MIS: begin
|
|
data_req_o = 1'b1;
|
|
// data_pmp_err_i is valid during the address phase of a request. An error will block the
|
|
// external request and so a data_gnt_i might never be signalled. The registered version
|
|
// pmp_err_q is only updated for new address phases and so can be used in WAIT_GNT* and
|
|
// WAIT_RVALID* states
|
|
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
|
|
// push out second request
|
|
data_req_o = 1'b1;
|
|
// tell ID/EX stage to update the address
|
|
addr_incr_req_o = 1'b1;
|
|
|
|
// first part rvalid is received, or gets a PMP error
|
|
if (data_rvalid_i || pmp_err_q) begin
|
|
// Update the PMP error for the second part
|
|
pmp_err_d = data_pmp_err_i;
|
|
// Record the error status of the first part
|
|
lsu_err_d = data_bus_err_i | pmp_err_q;
|
|
// Capture the first rdata for loads
|
|
rdata_update = ~data_we_q;
|
|
// If already granted, wait for second rvalid
|
|
ls_fsm_ns = data_gnt_i ? IDLE : WAIT_GNT;
|
|
// Update the address for the second part, if no error
|
|
addr_update = data_gnt_i & ~(data_bus_err_i | pmp_err_q);
|
|
// clear handle_misaligned if second request is granted
|
|
handle_misaligned_d = ~data_gnt_i;
|
|
end else begin
|
|
// first part rvalid is NOT received
|
|
if (data_gnt_i) begin
|
|
// second grant is received
|
|
ls_fsm_ns = WAIT_RVALID_MIS_GNTS_DONE;
|
|
handle_misaligned_d = 1'b0;
|
|
end
|
|
end
|
|
end
|
|
|
|
WAIT_GNT: begin
|
|
// tell ID/EX stage to update the address
|
|
addr_incr_req_o = handle_misaligned_q;
|
|
data_req_o = 1'b1;
|
|
if (data_gnt_i || pmp_err_q) begin
|
|
ctrl_update = 1'b1;
|
|
// Update the address, unless there was an error
|
|
addr_update = ~lsu_err_q;
|
|
ls_fsm_ns = IDLE;
|
|
handle_misaligned_d = 1'b0;
|
|
end
|
|
end
|
|
|
|
WAIT_RVALID_MIS_GNTS_DONE: begin
|
|
// tell ID/EX stage to update the address (to make sure the
|
|
// second address can be captured correctly for mtval and PMP checking)
|
|
addr_incr_req_o = 1'b1;
|
|
// Wait for the first rvalid, second request is already granted
|
|
if (data_rvalid_i) begin
|
|
// Update the pmp error for the second part
|
|
pmp_err_d = data_pmp_err_i;
|
|
// The first part cannot see a PMP error in this state
|
|
lsu_err_d = data_bus_err_i;
|
|
// Now we can update the address for the second part if no error
|
|
addr_update = ~data_bus_err_i;
|
|
// Capture the first rdata for loads
|
|
rdata_update = ~data_we_q;
|
|
// Wait for second rvalid
|
|
ls_fsm_ns = IDLE;
|
|
end
|
|
end
|
|
|
|
default: begin
|
|
ls_fsm_ns = IDLE;
|
|
end
|
|
endcase
|
|
end
|
|
|
|
assign lsu_req_done_o = (lsu_req_i | (ls_fsm_cs != IDLE)) & (ls_fsm_ns == IDLE);
|
|
|
|
// registers for FSM
|
|
always_ff @(posedge clk_i or negedge rst_ni) begin
|
|
if (!rst_ni) begin
|
|
ls_fsm_cs <= IDLE;
|
|
handle_misaligned_q <= '0;
|
|
pmp_err_q <= '0;
|
|
lsu_err_q <= '0;
|
|
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
|
|
end
|
|
|
|
/////////////
|
|
// Outputs //
|
|
/////////////
|
|
|
|
assign data_or_pmp_err = lsu_err_q | data_bus_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 & ~data_intg_err;
|
|
|
|
// output to register file
|
|
assign lsu_rdata_o = data_rdata_ext;
|
|
|
|
// output data address must be word aligned
|
|
assign data_addr_w_aligned = {data_addr[31:2], 2'b00};
|
|
|
|
// output to data interface
|
|
assign data_addr_o = data_addr_w_aligned;
|
|
assign data_we_o = lsu_we_i;
|
|
assign data_be_o = data_be;
|
|
|
|
/////////////////////////////////////
|
|
// Write data integrity generation //
|
|
/////////////////////////////////////
|
|
|
|
// SEC_CM: BUS.INTEGRITY
|
|
if (MemECC) begin : g_mem_wdata_ecc
|
|
prim_secded_inv_39_32_enc u_data_gen (
|
|
.data_i (data_wdata),
|
|
.data_o (data_wdata_o)
|
|
);
|
|
end else begin : g_no_mem_wdata_ecc
|
|
assign data_wdata_o = data_wdata;
|
|
end
|
|
|
|
// output to ID stage: mtval + AGU for misaligned transactions
|
|
assign addr_last_o = addr_last_q;
|
|
|
|
// Signal a load or store error depending on the transaction type outstanding
|
|
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;
|
|
// Integrity errors are their own category for timing reasons. load_err_o is factored directly
|
|
// into data_req_o to enable synchronous exception on load errors without performance loss (An
|
|
// upcoming load cannot request until the current load has seen its response, so the earliest
|
|
// point the new request can be sent is the same cycle the response is seen). If load_err_o isn't
|
|
// factored into data_req_o there would have to be a stall cycle between all back to back loads.
|
|
// The data_intg_err signal is generated combinatorially from the incoming data_rdata_i. Were it
|
|
// to be factored into load_err_o there would be a feedthrough path from data_rdata_i to
|
|
// data_req_o which is undesirable.
|
|
assign load_resp_intg_err_o = data_intg_err & data_rvalid_i & ~data_we_q;
|
|
assign store_resp_intg_err_o = data_intg_err & data_rvalid_i & data_we_q;
|
|
|
|
assign busy_o = (ls_fsm_cs != IDLE);
|
|
|
|
//////////
|
|
// FCOV //
|
|
//////////
|
|
`ifndef DV_FCOV_DISABLE
|
|
// Set when awaiting the response for the second half of a misaligned access
|
|
logic fcov_mis_2_en_d, fcov_mis_2_en_q;
|
|
|
|
// fcov_mis_rvalid_1: Set when the response is received to the first half of a misaligned access,
|
|
// fcov_mis_rvalid_2: Set when response is received for the second half
|
|
logic fcov_mis_rvalid_1, fcov_mis_rvalid_2;
|
|
|
|
// Set when the first half of a misaligned access saw a bus errror
|
|
logic fcov_mis_bus_err_1_d, fcov_mis_bus_err_1_q;
|
|
|
|
assign fcov_mis_rvalid_1 = ls_fsm_cs inside {WAIT_RVALID_MIS, WAIT_RVALID_MIS_GNTS_DONE} &&
|
|
data_rvalid_i;
|
|
|
|
assign fcov_mis_rvalid_2 = ls_fsm_cs inside {IDLE} && fcov_mis_2_en_q && data_rvalid_i;
|
|
|
|
assign fcov_mis_2_en_d = fcov_mis_rvalid_2 ? 1'b0 : // clr
|
|
fcov_mis_rvalid_1 ? 1'b1 : // set
|
|
fcov_mis_2_en_q ;
|
|
|
|
assign fcov_mis_bus_err_1_d = fcov_mis_rvalid_2 ? 1'b0 : // clr
|
|
fcov_mis_rvalid_1 && data_bus_err_i ? 1'b1 : // set
|
|
fcov_mis_bus_err_1_q ;
|
|
|
|
always_ff @(posedge clk_i or negedge rst_ni) begin
|
|
if (!rst_ni) begin
|
|
fcov_mis_2_en_q <= 1'b0;
|
|
fcov_mis_bus_err_1_q <= 1'b0;
|
|
end else begin
|
|
fcov_mis_2_en_q <= fcov_mis_2_en_d;
|
|
fcov_mis_bus_err_1_q <= fcov_mis_bus_err_1_d;
|
|
end
|
|
end
|
|
`endif
|
|
|
|
`DV_FCOV_SIGNAL(logic, ls_error_exception, (load_err_o | store_err_o) & ~pmp_err_q)
|
|
`DV_FCOV_SIGNAL(logic, ls_pmp_exception, (load_err_o | store_err_o) & pmp_err_q)
|
|
`DV_FCOV_SIGNAL(logic, ls_first_req, lsu_req_i & (ls_fsm_cs == IDLE))
|
|
`DV_FCOV_SIGNAL(logic, ls_second_req,
|
|
(ls_fsm_cs inside {WAIT_GNT, WAIT_RVALID_MIS}) & data_req_o & addr_incr_req_o)
|
|
`DV_FCOV_SIGNAL(logic, ls_mis_pmp_err_1,
|
|
(ls_fsm_cs inside {WAIT_RVALID_MIS, WAIT_GNT_MIS}) && pmp_err_q)
|
|
`DV_FCOV_SIGNAL(logic, ls_mis_pmp_err_2,
|
|
(ls_fsm_cs inside {WAIT_RVALID_MIS, WAIT_RVALID_MIS_GNTS_DONE}) && data_pmp_err_i)
|
|
|
|
////////////////
|
|
// Assertions //
|
|
////////////////
|
|
|
|
// Selectors must be known/valid.
|
|
`ASSERT(IbexDataTypeKnown, (lsu_req_i | busy_o) |-> !$isunknown(lsu_type_i))
|
|
`ASSERT(IbexDataOffsetKnown, (lsu_req_i | busy_o) |-> !$isunknown(data_offset))
|
|
`ASSERT_KNOWN(IbexRDataOffsetQKnown, rdata_offset_q)
|
|
`ASSERT_KNOWN(IbexDataTypeQKnown, data_type_q)
|
|
`ASSERT(IbexLsuStateValid, ls_fsm_cs inside {
|
|
IDLE, WAIT_GNT_MIS, WAIT_RVALID_MIS, WAIT_GNT,
|
|
WAIT_RVALID_MIS_GNTS_DONE})
|
|
|
|
// Address must not contain X when request is sent.
|
|
`ASSERT(IbexDataAddrUnknown, data_req_o |-> !$isunknown(data_addr_o))
|
|
|
|
// Address must be word aligned when request is sent.
|
|
`ASSERT(IbexDataAddrUnaligned, data_req_o |-> (data_addr_o[1:0] == 2'b00))
|
|
|
|
endmodule
|