| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| // |
| // Flash Controllber Arbiter for read |
| // |
| |
| `include "prim_assert.sv" |
| |
| // flash read and erase functionality is shared betewen the hardware (life cycle |
| // and key manager) and software interfaces. |
| // |
| // This module arbitrates and muxes the controls between the two interfaces. |
| |
| module flash_ctrl_arb import flash_ctrl_pkg::*; ( |
| input clk_i, |
| input rst_ni, |
| |
| input prim_mubi_pkg::mubi4_t disable_i, |
| |
| // error address shared between interfaces |
| output logic [BusAddrW-1:0] ctrl_err_addr_o, |
| |
| // software interface to rd_ctrl / erase_ctrl |
| input flash_ctrl_reg_pkg::flash_ctrl_reg2hw_control_reg_t sw_ctrl_i, |
| input [BusAddrByteW-1:0] sw_addr_i, |
| output logic sw_ack_o, |
| output flash_ctrl_err_t sw_err_o, |
| |
| // software interface to prog_fifo |
| input sw_wvalid_i, |
| output logic sw_wready_o, |
| input [BusFullWidth-1:0] sw_wdata_i, |
| |
| // hardware interface to rd_ctrl / erase_ctrl |
| input hw_req_i, |
| input flash_ctrl_reg_pkg::flash_ctrl_reg2hw_control_reg_t hw_ctrl_i, |
| input flash_lcmgr_phase_e hw_phase_i, |
| input [BusAddrByteW-1:0] hw_addr_i, |
| output logic hw_ack_o, |
| output flash_ctrl_err_t hw_err_o, |
| |
| // hardware interface to prog_fifo |
| input hw_wvalid_i, |
| input [BusFullWidth-1:0] hw_wdata_i, |
| output logic hw_wready_o, |
| |
| // muxed interface to rd_ctrl / erase_ctrl |
| output flash_ctrl_reg_pkg::flash_ctrl_reg2hw_control_reg_t muxed_ctrl_o, |
| output logic [BusAddrByteW-1:0] muxed_addr_o, |
| input prog_ack_i, |
| input flash_ctrl_err_t prog_err_i, |
| input [BusAddrW-1:0] prog_err_addr_i, |
| input rd_ack_i, |
| input flash_ctrl_err_t rd_err_i, |
| input [BusAddrW-1:0] rd_err_addr_i, |
| input erase_ack_i, |
| input flash_ctrl_err_t erase_err_i, |
| input [BusAddrW-1:0] erase_err_addr_i, |
| |
| // muxed interface to prog_fifo |
| output logic prog_fifo_wvalid_o, |
| output logic [BusFullWidth-1:0] prog_fifo_wdata_o, |
| input logic prog_fifo_wready_i, |
| |
| // flash phy initialization ongoing |
| input logic flash_phy_busy_i, |
| |
| // clear fifo contents |
| output logic fifo_clr_o, |
| |
| // output to memory protection |
| output flash_lcmgr_phase_e phase_o, |
| |
| // indication that sw has been selected |
| output flash_sel_e sel_o, |
| |
| // fsm sparse error |
| output logic fsm_err_o |
| |
| ); |
| |
| // arbitration FSM |
| // Encoding generated with: |
| // $ ./util/design/sparse-fsm-encode.py -d 5 -m 5 -n 10 \ |
| // -s 1044018132 --language=sv --avoid-zero |
| // |
| // Hamming distance histogram: |
| // |
| // 0: -- |
| // 1: -- |
| // 2: -- |
| // 3: -- |
| // 4: -- |
| // 5: |||||||||||||||||||| (50.00%) |
| // 6: |||||||||||||||| (40.00%) |
| // 7: |||| (10.00%) |
| // 8: -- |
| // 9: -- |
| // 10: -- |
| // |
| // Minimum Hamming distance: 5 |
| // Maximum Hamming distance: 7 |
| // Minimum Hamming weight: 5 |
| // Maximum Hamming weight: 7 |
| // |
| localparam int StateWidth = 10; |
| typedef enum logic [StateWidth-1:0] { |
| StReset = 10'b0011010110, |
| StHw = 10'b1111101110, |
| StSwActive = 10'b1100101001, |
| StSwIdle = 10'b1000000010, |
| StDisabled = 10'b0100010101 |
| } arb_state_e; |
| |
| flash_sel_e func_sel; |
| arb_state_e state_q, state_d; |
| |
| logic sw_req; |
| assign sw_req = sw_ctrl_i.start.q; |
| |
| // SEC_CM: CTRL.FSM.SPARSE |
| `PRIM_FLOP_SPARSE_FSM(u_state_regs, state_d, state_q, arb_state_e, StReset) |
| |
| always_comb begin |
| |
| func_sel = NoneSel; |
| fifo_clr_o = 1'b0; |
| state_d = state_q; |
| fsm_err_o = 1'b0; |
| |
| unique case (state_q) |
| StReset: begin |
| // until the flash phy is done with its own iniitlization, |
| // no flash controller activity is allowed to commence |
| if (!flash_phy_busy_i) begin |
| // after flash is ready, the HW interface always takes |
| // precedence for flash control initiliazation |
| state_d = StHw; |
| end |
| end |
| |
| StHw: begin |
| func_sel = HwSel; |
| |
| if (!hw_req_i) begin |
| state_d = StSwIdle; |
| end |
| end |
| |
| StSwIdle: begin |
| // software is still selected to enable access to Fifos |
| func_sel = SwSel; |
| |
| if (prim_mubi_pkg::mubi4_test_true_loose(disable_i)) begin |
| // Do not randomly switch unless idle as it may cause stateful operations to be |
| // disturbed |
| state_d = StDisabled; |
| end else if (hw_req_i) begin |
| // if hardware request comes in the middle, wipe fifos and enable |
| // switch to hardware interface |
| fifo_clr_o = 1'b1; |
| state_d = StHw; |
| end else if (sw_req) begin |
| state_d = StSwActive; |
| end |
| end |
| |
| StSwActive: begin |
| func_sel = SwSel; |
| |
| // Stay in software active mode until operation completes. |
| // While operations are ongoing, the interface cannot be switched |
| if (prog_ack_i || rd_ack_i || erase_ack_i) begin |
| state_d = StSwIdle; |
| end |
| end |
| |
| StDisabled: begin |
| state_d = StDisabled; |
| end |
| |
| default: begin |
| fsm_err_o = 1'b1; |
| end |
| endcase // unique case (state_q) |
| |
| end // always_comb |
| |
| logic ctrl_ack; |
| flash_ctrl_err_t ctrl_err; |
| |
| always_comb begin |
| muxed_ctrl_o = '0; |
| muxed_addr_o = '0; |
| sw_ack_o = '0; |
| sw_err_o = '0; |
| sw_wready_o = '0; |
| hw_ack_o = '0; |
| hw_err_o = '0; |
| |
| hw_wready_o = '0; |
| prog_fifo_wvalid_o = '0; |
| prog_fifo_wdata_o = '0; |
| |
| unique case (func_sel) |
| HwSel: begin |
| // ctrl related muxing |
| muxed_ctrl_o = hw_ctrl_i; |
| muxed_addr_o = hw_addr_i; |
| hw_ack_o = ctrl_ack; |
| hw_err_o = ctrl_err; |
| |
| // fifo related muxing |
| hw_wready_o = prog_fifo_wready_i; |
| prog_fifo_wvalid_o = hw_wvalid_i; |
| prog_fifo_wdata_o = hw_wdata_i; |
| end |
| |
| SwSel: begin |
| |
| // ctrl related muxing |
| muxed_ctrl_o = sw_ctrl_i; |
| muxed_addr_o = sw_addr_i; |
| sw_ack_o = ctrl_ack; |
| sw_err_o = ctrl_err; |
| |
| // fifo related muxing |
| sw_wready_o = prog_fifo_wready_i; |
| prog_fifo_wvalid_o = sw_wvalid_i; |
| prog_fifo_wdata_o = sw_wdata_i; |
| end |
| |
| default:; |
| endcase // unique case (func_sel) |
| end |
| |
| // pick appropriate feedback |
| always_comb begin |
| ctrl_ack = '0; |
| ctrl_err = '0; |
| ctrl_err_addr_o = '0; |
| |
| unique case (muxed_ctrl_o.op.q) |
| FlashOpProgram: begin |
| ctrl_ack = prog_ack_i; |
| ctrl_err = prog_err_i; |
| ctrl_err_addr_o = prog_err_addr_i; |
| end |
| |
| FlashOpErase: begin |
| ctrl_ack = erase_ack_i; |
| ctrl_err = erase_err_i; |
| ctrl_err_addr_o = erase_err_addr_i; |
| end |
| |
| FlashOpRead: begin |
| ctrl_ack = rd_ack_i; |
| ctrl_err = rd_err_i; |
| ctrl_err_addr_o = rd_err_addr_i; |
| end |
| |
| default: begin |
| // if operation is started but does not match |
| // any valid operation, error back |
| if (muxed_ctrl_o.start) begin |
| ctrl_ack = 1'b1; |
| ctrl_err.invalid_op_err = 1'b1; |
| end |
| end |
| |
| endcase // unique case (muxed_ctrl_o.op.q) |
| end |
| |
| assign sel_o = func_sel; |
| |
| // At the moment there is no software control indicating phase. |
| assign phase_o = func_sel == SwSel ? PhaseInvalid : hw_phase_i; |
| |
| endmodule // flash_ctrl_rd_arb |