blob: da8f947bac20aac031c366dab39b5c3f18512d58 [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 Module
//
//
// Flash phy represents the top level open source wrapper for a proprietary flash
// module.
// The top level flash_phy is only responsible for dispatching transactions and
// correctly collecting the responses in order.
module flash_phy import flash_ctrl_pkg::*; (
input clk_i,
input rst_ni,
input host_req_i,
input host_intg_err_i,
input tlul_pkg::tl_type_e host_req_type_i,
input [BusAddrW-1:0] host_addr_i,
output logic host_req_rdy_o,
output logic host_req_done_o,
output logic [BusWidth-1:0] host_rdata_o,
output logic host_rderr_o,
input flash_req_t flash_ctrl_i,
output flash_rsp_t flash_ctrl_o,
input tlul_pkg::tl_h2d_t tl_i,
output tlul_pkg::tl_d2h_t tl_o,
input lc_ctrl_pkg::lc_tx_t scanmode_i,
input scan_en_i,
input scan_rst_ni,
input flash_power_ready_h_i,
input flash_power_down_h_i,
inout [1:0] flash_test_mode_a_io,
inout flash_test_voltage_h_io,
input lc_ctrl_pkg::lc_tx_t flash_bist_enable_i,
input lc_ctrl_pkg::lc_tx_t lc_nvm_debug_en_i,
output ast_pkg::ast_dif_t flash_alert_o
);
// Flash macro outstanding refers to how many reads we allow a macro to move ahead of an
// in order blocking read. Since the data cannot be returned out of order, this simply
// does the reads in advance and store them in a FIFO
localparam int FlashMacroOustanding = 1;
localparam int SeqFifoDepth = FlashMacroOustanding * NumBanks;
// flash_phy forwards incoming host transactions to the appropriate bank. However, depending
// on the transaction type, the completion times may differ (for example, a transaction
// requiring de-scramble will take significantly longer than one that hits in the read buffers).
// This implies that it is possible for flash_phy to forward one transaction to bank N and another
// to bank N+1 only for bank N+1 to finish its transaction first.
//
// This suggests that even though transactions are received in-order, they can complete out of
// order. Thus it is the responsibility of the flash_phy to sequence the responses correctly.
// For banks that have finished ahead of time, it is also important to hold their output until
// consumption by the host.
//
// The sequence fifo below holds the correct response order, while each flash_phy_core is
// paired with a small passthrough response FIFO to hold the data if necessary.
// If one bank finishes "ahead" of schedule, the response FIFO will hold the response, and no new
// transactions will be issued to that bank until the response is consumed by the host.
// host to flash_phy interface
logic [BankW-1:0] host_bank_sel;
logic [BankW-1:0] rsp_bank_sel;
logic [NumBanks-1:0] host_req_rdy;
logic [NumBanks-1:0] host_req_done;
logic [NumBanks-1:0] host_rsp_avail;
logic [NumBanks-1:0] host_rsp_vld;
logic [NumBanks-1:0] host_rsp_ack;
logic [BusWidth-1:0] host_rsp_data [NumBanks];
logic [NumBanks-1:0] host_rsp_err;
logic seq_fifo_rdy;
logic seq_fifo_pending;
// flash_ctrl to flash_phy interface
logic [BankW-1:0] ctrl_bank_sel;
logic [NumBanks-1:0] rd_done;
logic [NumBanks-1:0] prog_done;
logic [NumBanks-1:0] erase_done;
logic init_busy;
logic [ProgTypes-1:0] prog_type_avail;
// common interface
logic [BusWidth-1:0] rd_data [NumBanks];
logic [NumBanks-1:0] rd_err;
// select which bank each is operating on
assign host_bank_sel = host_req_i ? host_addr_i[BusAddrW-1 -: BankW] : '0;
assign ctrl_bank_sel = flash_ctrl_i.addr[BusAddrW-1 -: BankW];
// accept transaction if bank is ready and previous response NOT pending
assign host_req_rdy_o = host_req_rdy[host_bank_sel] & host_rsp_avail[host_bank_sel] &
seq_fifo_rdy;
assign host_req_done_o = seq_fifo_pending & host_rsp_vld[rsp_bank_sel];
assign host_rderr_o = host_rsp_err[rsp_bank_sel];
assign host_rdata_o = host_rsp_data[rsp_bank_sel];
// all banks are assumed to be the same in terms of prog_type support
assign flash_ctrl_o.prog_type_avail = prog_type_avail;
assign flash_ctrl_o.rd_done = rd_done[ctrl_bank_sel];
assign flash_ctrl_o.prog_done = prog_done[ctrl_bank_sel];
assign flash_ctrl_o.erase_done = erase_done[ctrl_bank_sel];
assign flash_ctrl_o.rd_data = rd_data[ctrl_bank_sel];
assign flash_ctrl_o.rd_err = rd_err[ctrl_bank_sel];
assign flash_ctrl_o.init_busy = init_busy;
// feed through host integrity error directly
assign flash_ctrl_o.intg_err = host_intg_err_i;
// This fifo holds the expected return order
prim_fifo_sync #(
.Width (BankW),
.Pass (0),
.Depth (SeqFifoDepth)
) u_bank_sequence_fifo (
.clk_i,
.rst_ni,
.clr_i (1'b0),
.wvalid_i(host_req_i & host_req_rdy_o),
.wready_o(seq_fifo_rdy),
.wdata_i (host_bank_sel),
.depth_o (),
.full_o (),
.rvalid_o(seq_fifo_pending),
.rready_i(host_req_done_o),
.rdata_o (rsp_bank_sel)
);
// Generate host scramble_en indication, broadcasted to all banks
localparam int TotalRegions = MpRegions + 1;
logic host_scramble_en, host_ecc_en;
data_region_attr_t region_attrs [TotalRegions];
mp_region_cfg_t region_cfg, unused_cfg;
for(genvar i = 0; i < TotalRegions; i++) begin : gen_region_attrs
assign region_attrs[i].phase = PhaseInvalid;
assign region_attrs[i].cfg = flash_ctrl_i.region_cfgs[i];
end
// the region decode only accepts page address
flash_mp_data_region_sel #(
.Regions(TotalRegions)
) u_region_sel (
.req_i(host_req_i),
.phase_i(PhaseInvalid),
.addr_i(host_addr_i[BusAddrW-1 -: AllPagesW]),
.region_attrs_i(region_attrs),
.sel_cfg_o(region_cfg)
);
// most attributes are unused
assign unused_cfg = region_cfg;
// only scramble/ecc attributes are looked at
assign host_scramble_en = region_cfg.scramble_en.q;
assign host_ecc_en = region_cfg.ecc_en.q;
// Prim flash to flash_phy_core connections
flash_phy_pkg::flash_phy_prim_flash_req_t [NumBanks-1:0] prim_flash_req;
flash_phy_pkg::flash_phy_prim_flash_rsp_t [NumBanks-1:0] prim_flash_rsp;
logic [NumBanks-1:0] ecc_single_err;
logic [NumBanks-1:0][BusAddrW-1:0] ecc_addr;
assign flash_ctrl_o.ecc_single_err = ecc_single_err;
assign flash_ctrl_o.ecc_addr = ecc_addr;
lc_ctrl_pkg::lc_tx_t [NumBanks-1:0] flash_disable;
prim_lc_sync #(
.NumCopies(NumBanks),
.AsyncOn(0)
) u_flash_disable_sync (
.clk_i('0),
.rst_ni('0),
.lc_en_i(flash_ctrl_i.flash_disable),
.lc_en_o(flash_disable)
);
for (genvar bank = 0; bank < NumBanks; bank++) begin : gen_flash_cores
// pop if the response came from the appropriate fifo
assign host_rsp_ack[bank] = host_req_done_o & (rsp_bank_sel == bank);
prim_fifo_sync #(
.Width (BusWidth + 1),
.Pass (1'b1),
.Depth (FlashMacroOustanding)
) u_host_rsp_fifo (
.clk_i,
.rst_ni,
.clr_i (1'b0),
.wvalid_i(host_req_done[bank]),
.wready_o(host_rsp_avail[bank]),
.wdata_i ({rd_err[bank], rd_data[bank]}),
.depth_o (),
.full_o (),
.rvalid_o(host_rsp_vld[bank]),
.rready_i(host_rsp_ack[bank]),
.rdata_o ({host_rsp_err[bank], host_rsp_data[bank]})
);
logic host_req;
logic ctrl_req;
assign host_req = host_req_i & (host_bank_sel == bank) & host_rsp_avail[bank];
assign ctrl_req = flash_ctrl_i.req & (ctrl_bank_sel == bank);
assign ecc_addr[bank][BusBankAddrW +: BankW] = bank;
flash_phy_core u_core (
.clk_i,
.rst_ni,
// integrity error is either from host or from controller
.intg_err_i(host_intg_err_i | flash_ctrl_i.intg_err),
.req_i(ctrl_req),
.scramble_en_i(flash_ctrl_i.scramble_en),
.ecc_en_i(flash_ctrl_i.ecc_en),
.he_en_i(flash_ctrl_i.he_en),
.ecc_multi_err_en_i(flash_ctrl_i.ecc_multi_err_en),
// host request must be suppressed if response fifo cannot hold more
// otherwise the flash_phy_core and flash_phy will get out of sync
.host_req_i(host_req),
.host_req_type_i,
.host_scramble_en_i(host_scramble_en),
.host_ecc_en_i(host_ecc_en),
.host_addr_i(host_addr_i[0 +: BusBankAddrW]),
.rd_i(flash_ctrl_i.rd),
.prog_i(flash_ctrl_i.prog),
.pg_erase_i(flash_ctrl_i.pg_erase),
.bk_erase_i(flash_ctrl_i.bk_erase),
.erase_suspend_req_i(flash_ctrl_i.erase_suspend),
.part_i(flash_ctrl_i.part),
.info_sel_i(flash_ctrl_i.info_sel),
.addr_i(flash_ctrl_i.addr[0 +: BusBankAddrW]),
.prog_data_i(flash_ctrl_i.prog_data),
.prog_last_i(flash_ctrl_i.prog_last),
.prog_type_i(flash_ctrl_i.prog_type),
.addr_key_i(flash_ctrl_i.addr_key),
.data_key_i(flash_ctrl_i.data_key),
.rand_addr_key_i(flash_ctrl_i.rand_addr_key),
.rand_data_key_i(flash_ctrl_i.rand_data_key),
.rd_buf_en_i(flash_ctrl_i.rd_buf_en),
.host_req_rdy_o(host_req_rdy[bank]),
.host_req_done_o(host_req_done[bank]),
.rd_done_o(rd_done[bank]),
.prog_done_o(prog_done[bank]),
.erase_done_o(erase_done[bank]),
.rd_data_o(rd_data[bank]),
.rd_err_o(rd_err[bank]),
.flash_disable_i(flash_disable[bank]),
.prim_flash_req_o(prim_flash_req[bank]),
.prim_flash_rsp_i(prim_flash_rsp[bank]),
.ecc_single_err_o(ecc_single_err[bank]),
.ecc_addr_o(ecc_addr[bank][BusBankAddrW-1:0])
);
end // block: gen_flash_banks
// life cycle handling
logic tdo;
lc_ctrl_pkg::lc_tx_t [FlashLcDftLast-1:0] lc_nvm_debug_en;
assign flash_ctrl_o.jtag_rsp.tdo = tdo & (lc_nvm_debug_en[FlashLcTdoSel] == lc_ctrl_pkg::On);
prim_lc_sync #(
.NumCopies(int'(FlashLcDftLast))
) u_lc_nvm_debug_en_sync (
.clk_i,
.rst_ni,
.lc_en_i(lc_nvm_debug_en_i),
.lc_en_o(lc_nvm_debug_en)
);
lc_ctrl_pkg::lc_tx_t bist_enable_qual;
assign bist_enable_qual = lc_ctrl_pkg::lc_tx_t'(flash_bist_enable_i &
lc_nvm_debug_en[FlashBistSel]);
prim_flash #(
.NumBanks(NumBanks),
.InfosPerBank(InfosPerBank),
.InfoTypes(InfoTypes),
.InfoTypesWidth(InfoTypesWidth),
.PagesPerBank(PagesPerBank),
.WordsPerPage(WordsPerPage),
.DataWidth(flash_phy_pkg::FullDataWidth),
.MetaDataWidth(MetaDataWidth)
) u_flash (
.clk_i,
.rst_ni,
.tl_i,
.tl_o,
.devmode_i(1'b1),
.flash_req_i(prim_flash_req),
.flash_rsp_o(prim_flash_rsp),
.prog_type_avail_o(prog_type_avail),
.init_busy_o(init_busy),
.tck_i(flash_ctrl_i.jtag_req.tck & (lc_nvm_debug_en[FlashLcTckSel] == lc_ctrl_pkg::On)),
.tdi_i(flash_ctrl_i.jtag_req.tdi & (lc_nvm_debug_en[FlashLcTdiSel] == lc_ctrl_pkg::On)),
.tms_i(flash_ctrl_i.jtag_req.tms & (lc_nvm_debug_en[FlashLcTmsSel] == lc_ctrl_pkg::On)),
.tdo_o(tdo),
.bist_enable_i(bist_enable_qual),
.scanmode_i,
.scan_en_i,
.scan_rst_ni,
.flash_power_ready_h_i,
.flash_power_down_h_i,
.flash_test_mode_a_io,
.flash_test_voltage_h_io,
.flash_err_o(flash_ctrl_o.flash_err),
// There alert signals are forwarded to both flash controller and ast
.fl_alert_src_o(flash_alert_o)
);
logic unused_alert;
assign unused_alert = flash_ctrl_i.alert_trig & flash_ctrl_i.alert_ack;
logic unused_trst_n;
assign unused_trst_n = flash_ctrl_i.jtag_req.trst_n;
assign flash_ctrl_o.jtag_rsp.tdo_oe = 1'b1;
//////////////////////////////////////////////
// Assertions, Assumptions, and Coverpoints //
/////////////////////////////////////////////
// Add some assertions regarding response ordering
endmodule // flash_phy