blob: b0c2e4443193684c06d4eccd395fa97ef78f4631 [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 Prog Control
//
module flash_ctrl_prog import flash_ctrl_pkg::*; (
input clk_i,
input rst_ni,
// Control Interface
input op_start_i,
input [11:0] op_num_words_i,
output logic op_done_o,
output flash_ctrl_err_t op_err_o,
input [BusAddrW-1:0] op_addr_i,
input op_addr_oob_i,
input flash_prog_e op_type_i,
input [ProgTypes-1:0] type_avail_i,
output logic [BusAddrW-1:0] op_err_addr_o,
output logic cnt_err_o,
// FIFO Interface
input data_rdy_i,
input [BusFullWidth-1:0] data_i,
output logic data_rd_o,
// Flash Macro Interface
output logic flash_req_o,
output logic [BusAddrW-1:0] flash_addr_o,
output logic flash_ovfl_o,
output logic [BusFullWidth-1:0] flash_data_o,
output logic flash_last_o, // last beat of prog data
output flash_prog_e flash_type_o,
input flash_done_i,
input flash_mp_err_i,
input flash_prog_intg_err_i
);
typedef enum logic {
StNorm = 'h0,
StErr = 'h1
} state_e;
state_e st_q, st_d;
logic [11:0] cnt;
logic cnt_hit;
logic [BusAddrW:0] int_addr;
logic txn_done;
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 <= StNorm;
end else begin
st_q <= st_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_rd_o) begin
// cnt <= cnt + 1'b1;
// end
//end
prim_count #(
.Width(12)
) u_cnt (
.clk_i,
.rst_ni,
.clr_i(op_start_i && op_done_o),
.set_i('0),
.set_cnt_i('0),
.incr_en_i(data_rd_o),
.decr_en_i(1'b0),
.step_i(12'h1),
.cnt_o(cnt),
.cnt_next_o(),
.err_o(cnt_err_o)
);
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);
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
// if the requested prog type is available
logic prog_type_avail;
assign prog_type_avail = type_avail_i[op_type_i];
// program resolution check
// if the incoming beat is larger than the maximum program resolution, error
// immediately and do not allow it to start.
localparam int WindowWidth = BusAddrW - BusPgmResWidth;
logic [WindowWidth-1:0] start_window, end_window;
logic [BusAddrW-1:0] end_addr;
logic pgm_res_err;
logic win_err;
assign end_addr = op_addr_i + BusAddrW'(op_num_words_i);
assign start_window = op_addr_i[BusAddrW-1:BusPgmResWidth];
assign end_window = end_addr[BusAddrW-1:BusPgmResWidth];
assign pgm_res_err = start_window != end_window;
assign win_err = pgm_res_err | op_addr_oob_i;
// when error'd, continue to drain all program fifo contents like normal operation
// if this is not done, software may fill up the fifo without anyone
// draining the contents, leading to a lockup
always_comb begin
st_d = st_q;
flash_req_o = 1'b0;
data_rd_o = 1'b0;
op_done_o = 1'b0;
op_err_d = op_err_q;
unique case (st_q)
// Note the address counter is incremented on tx_done
// and cleared when the entire operation is complete.
StNorm: begin
if (cnt_err_o) begin
// if count error'd don't bother doing anything else, just try to finish
st_d = StErr;
end else if (op_start_i && prog_type_avail && !win_err) begin
// if the select operation type is not available, error
flash_req_o = data_rdy_i;
if (txn_done) begin
op_err_d.mp_err = flash_mp_err_i;
op_err_d.prog_err = flash_prog_intg_err_i;
data_rd_o = 1'b1;
if (cnt_hit) begin
op_done_o = 1'b1;
end else begin
st_d = |op_err_d ? StErr : StNorm;
end
end
end else if (op_start_i && (!prog_type_avail || win_err)) begin
op_err_d.oob_err = op_addr_oob_i;
op_err_d.prog_type_err = !prog_type_avail;
op_err_d.prog_win_err = pgm_res_err;
st_d = StErr;
end
end
StErr: begin
data_rd_o = data_rdy_i;
if (data_rdy_i && cnt_hit) begin
st_d = StNorm;
op_done_o = 1'b1;
end
end
default:;
endcase // unique case (st)
end
assign flash_data_o = data_i;
assign int_addr = op_addr_i + BusAddrW'(cnt);
assign flash_addr_o = int_addr[0 +: BusAddrW];
assign flash_ovfl_o = int_addr[BusAddrW];
assign flash_last_o = flash_req_o & cnt_hit;
assign flash_type_o = op_type_i;
assign op_err_o = op_err_q | op_err_d;
// unused signals
logic [BusPgmResWidth-1:0] unused_end_addr;
assign unused_end_addr = end_addr[BusPgmResWidth-1:0];
endmodule // flash_ctrl_prog