blob: beed731168d0f58881df265edbcb3838ffc553e8 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Flash Phy Prog Module
//
// This module implements the flash phy program operation
//
// The flash phy prog module is mainly responsible for packing incoming data
// transactions into appropriate flash word sizes.
//
// This is done primarily for two reasons
// - Reduce program stress on the flash
// Flash modules usually have a limit to how many times adjacent words can be programmed.
// If a programming beat is longer than a flash word, it would be best to compact those
// beats into multiples of the flash word size to improve performance and reduce
// unecessary programmings
//
// - Observe minimum block cipher sizes for scrambling / descrambling and ECC.
// Scrambling algorithms and ECC work on a specific chunk of data. When these features
// are enabled, the phy controller needs to ensure there is enough data to satisfy that
// request - TBD: This should be checked with software. Hardware could also always behave
// as it does in the point above and rely on software to correctly compact the data.
module flash_phy_prog import flash_phy_pkg::*; (
input clk_i,
input rst_ni,
input req_i,
input scramble_i,
input ecc_i,
input [WordSelW-1:0] sel_i,
input [BusWidth-1:0] data_i,
input last_i,
input ack_i, // ack means request has been accepted by flash
input done_i, // done means requested transaction has completed
input calc_ack_i,
input scramble_ack_i,
input [DataWidth-1:0] mask_i,
input [DataWidth-1:0] scrambled_data_i,
output logic calc_req_o,
output logic scramble_req_o,
output logic req_o,
output logic last_o, // last beat of an incoming transaction
output logic ack_o,
// block data does not contain ecc / metadata portion
output logic [DataWidth-1:0] block_data_o,
output logic [FullDataWidth-1:0] data_o
);
typedef enum logic [3:0] {
StIdle,
StPrePack,
StPackData,
StPostPack,
StReqFlash,
StWaitFlash,
StCalcMask,
StScrambleData,
StCalcEcc
} prog_state_e;
typedef enum logic [1:0] {
Filler,
Actual
} data_sel_e;
prog_state_e state_d, state_q;
// The currently observed data beat
logic [WordSelW-1:0] idx;
logic [WordSelW-1:0] idx_sub_one;
logic pack_valid;
logic [BusWidth-1:0] pack_data;
logic align_next;
data_sel_e data_sel;
localparam bit [WordSelW-1:0] MaxIdx = WordSelW'(WidthMultiple - 1);
logic [WidthMultiple-1:0][BusWidth-1:0] packed_data;
// selects empty data or real data
assign pack_data = (data_sel == Actual) ? data_i : {BusWidth{1'b1}};
// next idx will be aligned
assign idx_sub_one = idx - 1'b1;
assign align_next = (idx > '0) ? (idx_sub_one == sel_i) : 1'b0;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
idx <= '0;
end else if (pack_valid && idx == MaxIdx) begin
// when a flash word is packed full, return index to 0
idx <= '0;
end else if (pack_valid) begin
// increment otherwise
idx <= idx + 1'b1;
end
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
state_q <= StIdle;
end else begin
state_q <= state_d;
end
end
// If the first beat of an incoming transaction is not aligned to word boundary (for example
// if each flash word is 4 bus words wide, and the first word to program starts at index 1),
// the fsm pre-packs the flash word with empty words until the supplied index.
// Once at the index, real data supplied from the flash controller is packed until the last
// beat of data. At the last beat of data, if it is not also aligned (index 3 in this example),
// more empty words are packed at the end to fill out the word.
//
always_comb begin
state_d = state_q;
pack_valid = 1'b0;
data_sel = Filler;
req_o = 1'b0;
ack_o = 1'b0;
last_o = 1'b0;
calc_req_o = 1'b0;
scramble_req_o = 1'b0;
unique case (state_q)
StIdle: begin
// if first beat of a transaction is not aligned, prepack with empty bits
if (req_i && |sel_i) begin
state_d = StPrePack;
end else if (req_i) begin
state_d = StPackData;
end
end
StPrePack: begin
// pack until currently supplied data
pack_valid = (idx < sel_i);
if (idx == align_next) begin
state_d = StPackData;
end
end
StPackData: begin
pack_valid = req_i;
data_sel = Actual;
if (req_i && idx == MaxIdx) begin
// last beat of a flash word
state_d = scramble_i ? StCalcMask : StReqFlash;
end else if (req_i && last_i) begin
// last beat is not aligned with the last entry of flash word
state_d = StPostPack;
end else if (req_i) begin
ack_o = 1'b1;
end
end
StPostPack: begin
// supply filler data
pack_valid = 1'b1;
data_sel = Filler;
// finish packing remaining entries
if (idx == MaxIdx) begin
state_d = scramble_i ? StCalcMask : StReqFlash;
end
end
StCalcMask: begin
calc_req_o = 1'b1;
if (calc_ack_i) begin
state_d = StScrambleData;
end
end
StScrambleData: begin
scramble_req_o = 1'b1;
if (scramble_ack_i) begin
state_d = StCalcEcc;
end
end
StCalcEcc: begin
state_d = StReqFlash;
end
StReqFlash: begin
req_o = 1'b1;
last_o = last_i;
// if this is the last beat of the program burst
// - wait for done
// if this is NOT the last beat
// - ack the upstream request and accept more beats
if (last_i) begin
state_d = ack_i ? StWaitFlash : StReqFlash;
end else begin
ack_o = ack_i;
state_d = ack_i ? StIdle : StReqFlash;
end
end
StWaitFlash: begin
if (done_i) begin
ack_o = 1'b1;
state_d = StIdle;
end
end
default:;
endcase // unique case (state_q)
end
logic [DataWidth-1:0] mask_q;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
packed_data <= '0;
mask_q <= '0;
end else if (req_o && ack_i) begin
packed_data <= '0;
end else if (calc_req_o && calc_ack_i) begin
packed_data <= packed_data ^ mask_i;
mask_q <= mask_i;
end else if (scramble_req_o && scramble_ack_i) begin
packed_data <= scrambled_data_i[DataWidth-1:0] ^ mask_q;
end else if (pack_valid) begin
packed_data[idx] <= pack_data;
end
end
assign block_data_o = packed_data;
// ECC handling
logic [ScrDataWidth-1:0] ecc_data;
prim_secded_hamming_72_64_enc u_enc (
.in(packed_data),
.out(ecc_data)
);
// pad the remaining bits to '0', this effectively "programs" them.
assign data_o = ecc_i ? FullDataWidth'(ecc_data) : FullDataWidth'(packed_data);
/////////////////////////////////
// Assertions
/////////////////////////////////
`ifndef SYNTHESIS
logic txn_done;
logic [15:0] done_cnt;
assign txn_done = req_i && ack_o && last_i;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (rst_ni) begin
done_cnt <= '0;
end else if (txn_done) begin
done_cnt <= '0;
end else if (done_i) begin
done_cnt <= done_cnt + 1'b1;
end
end
// We can only observe one done per transaction.
`ASSERT(OneDonePerTxn_A, txn_done |-> done_cnt == '0)
`endif
// Prepack state can only pack up to WidthMultiple - 1
`ASSERT(PrePackRule_A, state_q == StPrePack && pack_valid |-> idx < MaxIdx)
// Postpack states should never pack the first index (as it would be aligned in that case)
`ASSERT(PostPackRule_A, state_q == StPostPack && pack_valid |-> idx != '0)
endmodule // flash_phy_prog