blob: d64cf8fe6a0413675d3d2c82f19b463072a9bf13 [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 [WordSelW-1:0] sel_i,
input [BusWidth-1:0] data_i,
input last_i,
input ack_i,
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 ack_o,
output logic [DataWidth-1:0] data_o
);
typedef enum logic [2:0] {
StIdle,
StPrePack,
StPackData,
StPostPack,
StWaitFlash,
StCalcMask,
StScrambleData
} 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 pack_valid;
logic [BusWidth-1:0] pack_data;
logic align_next;
data_sel_e data_sel;
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 align_next = (idx > '0) ? (idx - 1'b1) == 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 == (WidthMultiple-1)) 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;
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 == (WidthMultiple-1)) begin
// last beat of a flash word
state_d = scramble_i ? StCalcMask : StWaitFlash;
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 == (WidthMultiple-1)) begin
state_d = scramble_i ? StCalcMask : StWaitFlash;
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 = StWaitFlash;
end
end
StWaitFlash: begin
req_o = 1'b1;
if (ack_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 ^ mask_q;
end else if (pack_valid) begin
packed_data[idx] <= pack_data;
end
end
assign data_o = packed_data;
/////////////////////////////////
// Assertions
/////////////////////////////////
// Prepack state can only pack up to WidthMultiple - 1
`ASSERT(PrePackRule_A, state_q == StPrePack && pack_valid |-> idx < (WidthMultiple - 1))
// 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