blob: 4973761a75da1fc69bf75841fb3bb1b807c7649b [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Faux Flash Read Control
//
module flash_ctrl_rd import flash_ctrl_pkg::*; (
input clk_i,
input rst_ni,
// Software Interface
input op_start_i,
input [11:0] op_num_words_i,
output logic op_done_o,
flash_ctrl_err_t op_err_o,
input [BusAddrW-1:0] op_addr_i,
input op_addr_oob_i,
output logic [BusAddrW-1:0] op_err_addr_o,
// FIFO Interface
input data_rdy_i,
output logic [BusWidth-1:0] data_o,
output logic data_wr_o,
// Flash Macro Interface
output logic flash_req_o,
output logic [BusAddrW-1:0] flash_addr_o,
output logic flash_ovfl_o,
input [BusWidth-1:0] flash_data_i,
input flash_done_i,
input flash_phy_err_i,
input flash_rd_err_i,
input flash_mp_err_i
);
typedef enum logic [1:0] {
StIdle,
StNorm,
StErr
} state_e;
state_e st_q, st_d;
logic [11:0] cnt;
logic cnt_hit;
logic [BusAddrW:0] int_addr;
logic txn_done;
logic err_sel; //1 selects error data, 0 selects normal data
flash_ctrl_err_t op_err_q, op_err_d;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
st_q <= StIdle;
end else begin
st_q <= st_d;
end
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
op_err_q <= '0;
end else if (op_start_i && op_done_o) begin
op_err_q <= '0;
end else begin
op_err_q <= op_err_d;
end
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
cnt <= '0;
end else if (op_start_i && op_done_o) begin
cnt <= '0;
end else if (data_wr_o) begin
cnt <= cnt + 1'b1;
end
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
op_err_addr_o <= '0;
end else if (~|op_err_q && |op_err_d) begin
op_err_addr_o <= flash_addr_o;
end
end
assign txn_done = flash_req_o & flash_done_i;
assign cnt_hit = (cnt == op_num_words_i);
// when error'd, continue to complete existing read transaction but fill in with all 1's
// if this is not done, software may continue to attempt to read out of the fifo
// and eventually cause a bus deadlock as the fifo would be empty
// This scheme is similar to burst completion up an error
always_comb begin
st_d = st_q;
flash_req_o = 1'b0;
data_wr_o = 1'b0;
op_done_o = 1'b0;
op_err_d = op_err_q;
unique case (st_q)
StIdle: begin
if (op_start_i) begin
op_err_d.oob_err = op_addr_oob_i;
st_d = |op_err_d ? StErr : StNorm;
end
end
// Note the address counter is incremented on tx_done
// and cleared when the entire operation is complete.
StNorm: begin
flash_req_o = op_start_i & data_rdy_i;
if (txn_done) begin
op_err_d.mp_err = flash_mp_err_i;
op_err_d.rd_err = flash_rd_err_i;
op_err_d.phy_err = flash_phy_err_i;
data_wr_o = 1'b1;
if (cnt_hit) begin
op_done_o = 1'b1;
st_d = StIdle;
end else begin
st_d = |op_err_d ? StErr : StNorm;
end
end
end
StErr: begin
data_wr_o = data_rdy_i;
if (data_rdy_i && cnt_hit) begin
st_d = StIdle;
op_done_o = 1'b1;
end
end
default:;
endcase // unique case (st)
end
// overflow error detection is not here, but instead handled at memory protection
assign int_addr = op_addr_i + BusAddrW'(cnt);
assign flash_addr_o = int_addr[0 +: BusAddrW];
assign flash_ovfl_o = int_addr[BusAddrW];
// if error, return "empty" data
assign err_sel = data_wr_o & |op_err_o;
assign data_o = err_sel ? {BusWidth{1'b1}} : flash_data_i;
assign op_err_o = op_err_q | op_err_d;
endmodule // flash_ctrl_rd