|  | // Copyright lowRISC contributors. | 
|  | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | // SPDX-License-Identifier: Apache-2.0 | 
|  | // | 
|  | // Power Manager Fast FSM | 
|  | // | 
|  |  | 
|  | `include "prim_assert.sv" | 
|  |  | 
|  | module pwrmgr_fsm import pwrmgr_pkg::*; import pwrmgr_reg_pkg::*;( | 
|  | input clk_i, | 
|  | input rst_ni, | 
|  | input clk_slow_i, | 
|  | input rst_slow_ni, | 
|  |  | 
|  | // interface with slow_fsm | 
|  | input req_pwrup_i, | 
|  | input pwrup_cause_e pwrup_cause_i, | 
|  | output logic ack_pwrup_o, | 
|  | output logic req_pwrdn_o, | 
|  | input ack_pwrdn_i, | 
|  | input low_power_entry_i, | 
|  | input main_pd_ni, | 
|  | input [TotalResetWidth-1:0] reset_reqs_i, | 
|  | input fsm_invalid_i, | 
|  | output logic clr_slow_req_o, | 
|  | input usb_ip_clk_en_i, | 
|  | output logic usb_ip_clk_status_o, | 
|  |  | 
|  | // consumed in pwrmgr | 
|  | output logic wkup_o,        // generate wake interrupt | 
|  | output logic fall_through_o, | 
|  | output logic abort_o, | 
|  | output logic clr_hint_o, | 
|  | output logic clr_cfg_lock_o, | 
|  |  | 
|  | // rstmgr | 
|  | output pwr_rst_req_t pwr_rst_o, | 
|  | input pwr_rst_rsp_t pwr_rst_i, | 
|  |  | 
|  | // clkmgr | 
|  | output pwr_clk_req_t ips_clk_en_o, | 
|  | input pwr_clk_rsp_t clk_en_status_i, | 
|  |  | 
|  | // otp | 
|  | output logic otp_init_o, | 
|  | input otp_done_i, | 
|  | input otp_idle_i, | 
|  |  | 
|  | // lc | 
|  | output logic lc_init_o, | 
|  | input lc_done_i, | 
|  | input lc_idle_i, | 
|  | input lc_ctrl_pkg::lc_tx_t lc_dft_en_i, | 
|  | input lc_ctrl_pkg::lc_tx_t lc_hw_debug_en_i, | 
|  |  | 
|  | // flash | 
|  | input flash_idle_i, | 
|  |  | 
|  | // rom_ctrl | 
|  | input prim_mubi_pkg::mubi4_t rom_ctrl_done_i, | 
|  | input prim_mubi_pkg::mubi4_t rom_ctrl_good_i, | 
|  |  | 
|  | // pinmux | 
|  | output logic strap_o, | 
|  | output logic low_power_o, | 
|  |  | 
|  | // processing elements | 
|  | output lc_ctrl_pkg::lc_tx_t fetch_en_o | 
|  | ); | 
|  |  | 
|  | import prim_mubi_pkg::mubi4_t; | 
|  | import prim_mubi_pkg::mubi4_test_true_strict; | 
|  | import prim_mubi_pkg::mubi4_or_hi; | 
|  | import prim_mubi_pkg::mubi4_and_hi; | 
|  | import lc_ctrl_pkg::lc_tx_and_hi; | 
|  | import lc_ctrl_pkg::lc_tx_test_true_strict; | 
|  |  | 
|  | // The code below always assumes the always on domain is index 0 | 
|  | `ASSERT_INIT(AlwaysOnIndex_A, ALWAYS_ON_DOMAIN == 0) | 
|  |  | 
|  | // when there are multiple on domains, the latter 1 should become another parameter | 
|  | localparam int OffDomainSelStart = ALWAYS_ON_DOMAIN + 1; | 
|  |  | 
|  | // all powered down domains have resets asserted | 
|  | logic pd_n_rsts_asserted; | 
|  |  | 
|  | // all domains have resets asserted | 
|  | logic all_rsts_asserted; | 
|  |  | 
|  | // resets are valid | 
|  | logic reset_valid; | 
|  |  | 
|  | // reset hint to rstmgr | 
|  | reset_cause_e reset_cause_q, reset_cause_d; | 
|  |  | 
|  | // reset request | 
|  | logic reset_req; | 
|  | logic direct_rst_req; | 
|  | logic ndmreset_req; | 
|  | logic hw_rst_req; | 
|  | logic sw_rst_req; | 
|  |  | 
|  | // strap sample should only happen on cold boot or when the | 
|  | // the system goes through a reset cycle | 
|  | logic strap_sampled; | 
|  |  | 
|  | // disable processing element fetching | 
|  | lc_ctrl_pkg::lc_tx_t fetch_en_q, fetch_en_d; | 
|  |  | 
|  | fast_pwr_state_e state_d, state_q; | 
|  | logic reset_ongoing_q, reset_ongoing_d; | 
|  | logic req_pwrdn_q, req_pwrdn_d; | 
|  | logic ack_pwrup_q, ack_pwrup_d; | 
|  | logic ip_clk_en_q, ip_clk_en_d; | 
|  | logic [PowerDomains-1:0] rst_lc_req_q, rst_sys_req_q; | 
|  | logic [PowerDomains-1:0] rst_lc_req_d, rst_sys_req_d; | 
|  | logic otp_init; | 
|  | logic lc_init; | 
|  | logic low_power_q, low_power_d; | 
|  |  | 
|  | assign pd_n_rsts_asserted = pwr_rst_i.rst_lc_src_n[PowerDomains-1:OffDomainSelStart] == '0 & | 
|  | pwr_rst_i.rst_sys_src_n[PowerDomains-1:OffDomainSelStart] == '0; | 
|  |  | 
|  | logic lc_rsts_valid; | 
|  | assign lc_rsts_valid = ((rst_lc_req_q & ~pwr_rst_i.rst_lc_src_n) | | 
|  | (~rst_lc_req_q & pwr_rst_i.rst_lc_src_n)) == {PowerDomains{1'b1}}; | 
|  | logic sys_rsts_valid; | 
|  | assign sys_rsts_valid = ((rst_sys_req_q & ~pwr_rst_i.rst_sys_src_n) | | 
|  | (~rst_sys_req_q & pwr_rst_i.rst_sys_src_n)) == {PowerDomains{1'b1}}; | 
|  |  | 
|  | assign all_rsts_asserted = lc_rsts_valid & sys_rsts_valid; | 
|  |  | 
|  | // Any reset request was asserted. | 
|  | assign reset_req = |reset_reqs_i; | 
|  |  | 
|  | // Any peripheral triggererd hardware reset request. | 
|  | assign hw_rst_req = |reset_reqs_i[NumRstReqs-1:0]; | 
|  |  | 
|  | // Direct reset request that bypass checks. | 
|  | assign direct_rst_req = reset_reqs_i[ResetEscIdx] | | 
|  | reset_reqs_i[ResetMainPwrIdx]; | 
|  |  | 
|  | // Ndm reset request. | 
|  | assign ndmreset_req = reset_reqs_i[ResetNdmIdx]; | 
|  |  | 
|  | // Software triggered reset request. | 
|  | assign sw_rst_req = reset_reqs_i[ResetSwReqIdx]; | 
|  |  | 
|  | // when in low power path, resets are controlled by domain power down | 
|  | // when in reset path, all resets must be asserted | 
|  | // when the reset cause is something else, it is invalid | 
|  | assign reset_valid = reset_cause_q == LowPwrEntry ? main_pd_ni | pd_n_rsts_asserted : | 
|  | reset_cause_q == HwReq       ? all_rsts_asserted : 1'b0; | 
|  |  | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin | 
|  | if (!rst_ni) begin | 
|  | ack_pwrup_q <= 1'b0; | 
|  | req_pwrdn_q <= 1'b0; | 
|  | reset_ongoing_q <= 1'b0; | 
|  | ip_clk_en_q <= 1'b0; | 
|  | rst_lc_req_q <= {PowerDomains{1'b1}}; | 
|  | rst_sys_req_q <= {PowerDomains{1'b1}}; | 
|  | reset_cause_q <= ResetUndefined; | 
|  | low_power_q <= 1'b1; | 
|  | end else begin | 
|  | ack_pwrup_q <= ack_pwrup_d; | 
|  | req_pwrdn_q <= req_pwrdn_d; | 
|  | reset_ongoing_q <= reset_ongoing_d; | 
|  | ip_clk_en_q <= ip_clk_en_d; | 
|  | rst_lc_req_q <= rst_lc_req_d; | 
|  | rst_sys_req_q <= rst_sys_req_d; | 
|  | reset_cause_q <= reset_cause_d; | 
|  | low_power_q <= low_power_d; | 
|  | end | 
|  | end | 
|  |  | 
|  | // SEC_CM: FSM.SPARSE | 
|  | `PRIM_FLOP_SPARSE_FSM(u_state_regs, state_d, state_q, fast_pwr_state_e, FastPwrStateLowPower) | 
|  |  | 
|  | always_ff @(posedge clk_i or negedge rst_ni) begin | 
|  | if (!rst_ni) begin | 
|  | strap_sampled <= 1'b0; | 
|  | end else if (&rst_sys_req_q) begin | 
|  | strap_sampled <= 1'b0; | 
|  | end else if (strap_o) begin | 
|  | strap_sampled <= 1'b1; | 
|  | end | 
|  | end | 
|  |  | 
|  | prim_lc_sender u_fetch_en ( | 
|  | .clk_i, | 
|  | .rst_ni, | 
|  | .lc_en_i(fetch_en_d), | 
|  | .lc_en_o(fetch_en_q) | 
|  | ); | 
|  | assign fetch_en_o = fetch_en_q; | 
|  |  | 
|  | // Life cycle broadcast may take time to propagate through the system. | 
|  | // The sync below simulates that behavior using the slowest clock in the | 
|  | // system. | 
|  | logic slow_lc_done; | 
|  | logic lc_done; | 
|  |  | 
|  | prim_flop_2sync #( | 
|  | .Width(1) | 
|  | ) u_slow_sync_lc_done ( | 
|  | .clk_i(clk_slow_i), | 
|  | .rst_ni(rst_slow_ni), | 
|  | .d_i(lc_done_i), | 
|  | .q_o(slow_lc_done) | 
|  | ); | 
|  |  | 
|  | prim_flop_2sync #( | 
|  | .Width(1) | 
|  | ) u_sync_lc_done ( | 
|  | .clk_i, | 
|  | .rst_ni, | 
|  | .d_i(slow_lc_done), | 
|  | .q_o(lc_done) | 
|  | ); | 
|  |  | 
|  |  | 
|  | logic clks_enabled; | 
|  | logic clks_disabled; | 
|  |  | 
|  | // clocks all enabled computed as follows: | 
|  | // if enable is high, meaning clock is requested to turn on, the status must | 
|  | // also be 1. | 
|  | // if enable is low, meaning clock is not requested to turn on, the status is | 
|  | // don't care. | 
|  | // the bit-wise OR of both conditions must be all true. | 
|  | assign clks_enabled = ip_clk_en_q && | 
|  | &((ips_clk_en_o & clk_en_status_i) | ~ips_clk_en_o); | 
|  |  | 
|  | // clocks all disabled is the opposite: | 
|  | // if enable is low the status must also be low. | 
|  | // if enable is high, the status is don't care. | 
|  | // the bit-wise OR of both conditions must be all true. | 
|  | assign clks_disabled = ~ip_clk_en_q && | 
|  | &((~ips_clk_en_o & ~clk_en_status_i) | ips_clk_en_o); | 
|  |  | 
|  |  | 
|  | // rom integrity checks are disabled during TEST / RMA states | 
|  | // During TEST / RMA states, both dft_en and hw_debug_en are On. | 
|  | // During DEV / PROD states, either both signals are Off, or only | 
|  | // hw_debug_en is On | 
|  |  | 
|  | mubi4_t rom_intg_chk_dis; | 
|  | assign rom_intg_chk_dis = lc_tx_test_true_strict(lc_tx_and_hi(lc_dft_en_i, lc_hw_debug_en_i)) ? | 
|  | prim_mubi_pkg::MuBi4True : | 
|  | prim_mubi_pkg::MuBi4False; | 
|  |  | 
|  | mubi4_t rom_intg_chk_done; | 
|  | mubi4_t rom_intg_chk_good; | 
|  | assign rom_intg_chk_done = mubi4_or_hi(mubi4_and_hi(rom_intg_chk_dis, rom_ctrl_done_i), | 
|  | rom_ctrl_done_i); | 
|  | assign rom_intg_chk_good = mubi4_or_hi(rom_intg_chk_dis, rom_ctrl_good_i); | 
|  |  | 
|  | always_comb begin | 
|  | otp_init = 1'b0; | 
|  | lc_init = 1'b0; | 
|  | wkup_o = 1'b0; | 
|  | fall_through_o = 1'b0; | 
|  | abort_o = 1'b0; | 
|  | clr_hint_o = 1'b0; | 
|  | clr_cfg_lock_o = 1'b0; | 
|  | strap_o = 1'b0; | 
|  | clr_slow_req_o = 1'b0; | 
|  |  | 
|  | state_d = state_q; | 
|  | ack_pwrup_d = ack_pwrup_q; | 
|  | req_pwrdn_d = req_pwrdn_q; | 
|  | reset_ongoing_d = reset_ongoing_q; | 
|  | ip_clk_en_d = ip_clk_en_q; | 
|  | rst_lc_req_d = rst_lc_req_q; | 
|  | rst_sys_req_d = rst_sys_req_q; | 
|  | reset_cause_d = reset_cause_q; | 
|  | low_power_d = low_power_q; | 
|  | fetch_en_d = fetch_en_q; | 
|  |  | 
|  | unique case(state_q) | 
|  |  | 
|  | FastPwrStateLowPower: begin | 
|  | if (req_pwrup_i || reset_ongoing_q) begin | 
|  | state_d = FastPwrStateEnableClocks; | 
|  | end | 
|  | end | 
|  |  | 
|  | FastPwrStateEnableClocks: begin | 
|  | ip_clk_en_d = 1'b1; | 
|  | if (clks_enabled) begin | 
|  | state_d = FastPwrStateReleaseLcRst; | 
|  | end | 
|  | end | 
|  |  | 
|  | FastPwrStateReleaseLcRst: begin | 
|  | rst_lc_req_d = '0;  // release rst_lc_n for all power domains | 
|  | rst_sys_req_d = '0; // release rst_sys_n for all power domains | 
|  | // once all resets are released continue to otp initilization | 
|  | if (&pwr_rst_i.rst_lc_src_n) begin | 
|  | state_d = FastPwrStateOtpInit; | 
|  | end | 
|  | end | 
|  |  | 
|  | FastPwrStateOtpInit: begin | 
|  | otp_init = 1'b1; | 
|  |  | 
|  | if (otp_done_i) begin | 
|  | state_d = FastPwrStateLcInit; | 
|  | end | 
|  | end | 
|  |  | 
|  | FastPwrStateLcInit: begin | 
|  | lc_init = 1'b1; | 
|  |  | 
|  | if (lc_done) begin | 
|  | state_d = FastPwrStateAckPwrUp; | 
|  |  | 
|  | end | 
|  | end | 
|  |  | 
|  | FastPwrStateAckPwrUp: begin | 
|  | // only ack the slow_fsm if we actually transitioned through it | 
|  | ack_pwrup_d = !reset_ongoing_q; | 
|  |  | 
|  | // wait for request power up to drop relative to ack | 
|  | if (!req_pwrup_i || reset_ongoing_q) begin | 
|  | ack_pwrup_d = 1'b0; | 
|  | clr_cfg_lock_o = 1'b1; | 
|  | // generate a wakeup interrupt if we intended to go to low power | 
|  | // and we were woken from low power with a wakeup and not reset | 
|  | wkup_o = (pwrup_cause_i == Wake) & (reset_cause_q == LowPwrEntry); | 
|  | // This constitutes the end of a reset cycle | 
|  | reset_ongoing_d = 1'b0; | 
|  | state_d = FastPwrStateStrap; | 
|  | end | 
|  | end | 
|  |  | 
|  | FastPwrStateStrap: begin | 
|  | strap_o = ~strap_sampled; | 
|  | state_d =  FastPwrStateRomCheckDone; | 
|  | end | 
|  |  | 
|  | FastPwrStateRomCheckDone: begin | 
|  | // zero outgoing low power indication | 
|  | low_power_d = '0; | 
|  | reset_cause_d = ResetNone; | 
|  |  | 
|  | // When done is observed, advance to good check | 
|  | if (mubi4_test_true_strict(rom_intg_chk_done)) begin | 
|  | state_d = FastPwrStateRomCheckGood; | 
|  | end | 
|  | end | 
|  |  | 
|  | FastPwrStateRomCheckGood: begin | 
|  | if (mubi4_test_true_strict(rom_intg_chk_good)) begin | 
|  | state_d = FastPwrStateActive; | 
|  | end | 
|  | end | 
|  |  | 
|  | FastPwrStateActive: begin | 
|  | // only in active state, allow processor to execute | 
|  | fetch_en_d = lc_ctrl_pkg::On; | 
|  |  | 
|  | // when handling reset request or low power entry of any | 
|  | // kind, stop processor from fetching | 
|  | if (reset_req || low_power_entry_i) begin | 
|  | fetch_en_d = lc_ctrl_pkg::Off; | 
|  | reset_cause_d = ResetUndefined; | 
|  | state_d = FastPwrStateDisClks; | 
|  | end | 
|  | end | 
|  |  | 
|  | FastPwrStateDisClks: begin | 
|  | ip_clk_en_d = 1'b0; | 
|  |  | 
|  | if (clks_disabled) begin | 
|  | state_d = reset_req ? FastPwrStateNvmShutDown : FastPwrStateFallThrough; | 
|  | low_power_d = ~reset_req; | 
|  | end else begin | 
|  | // escalation was received, skip all handshaking and directly reset | 
|  | state_d = direct_rst_req ? FastPwrStateNvmShutDown : state_q; | 
|  | low_power_d = ~reset_req; | 
|  | end | 
|  | end | 
|  |  | 
|  | // Low Power Path | 
|  | FastPwrStateFallThrough: begin | 
|  | clr_hint_o = 1'b1; | 
|  |  | 
|  | // The processor was interrupted after it asserted WFI and is executing again | 
|  | if (!low_power_entry_i) begin | 
|  | ip_clk_en_d = 1'b1; | 
|  | wkup_o = 1'b1; | 
|  | fall_through_o = 1'b1; | 
|  | state_d = FastPwrStateRomCheckDone; | 
|  | end else begin | 
|  | state_d = FastPwrStateNvmIdleChk; | 
|  | end | 
|  | end | 
|  |  | 
|  | FastPwrStateNvmIdleChk: begin | 
|  |  | 
|  | if (otp_idle_i && lc_idle_i && flash_idle_i) begin | 
|  | state_d = FastPwrStateLowPowerPrep; | 
|  | end else begin | 
|  | ip_clk_en_d = 1'b1; | 
|  | wkup_o = 1'b1; | 
|  | abort_o = 1'b1; | 
|  | state_d = FastPwrStateRomCheckDone; | 
|  | end | 
|  | end | 
|  |  | 
|  | FastPwrStateLowPowerPrep: begin | 
|  | // reset cause is set only if main power domain will be turned off | 
|  | reset_cause_d = LowPwrEntry; | 
|  |  | 
|  | // reset non-always-on domains if requested | 
|  | // this includes the clock manager, which implies pwr/rst managers must | 
|  | // be fed directly from the source | 
|  | for (int i = OffDomainSelStart; i < PowerDomains; i++) begin | 
|  | rst_lc_req_d[i] = ~main_pd_ni; | 
|  | rst_sys_req_d[i] = ~main_pd_ni; | 
|  | end | 
|  |  | 
|  | if (reset_valid) begin | 
|  | state_d = FastPwrStateReqPwrDn; | 
|  | end | 
|  | end | 
|  |  | 
|  | FastPwrStateReqPwrDn: begin | 
|  | req_pwrdn_d = 1'b1; | 
|  |  | 
|  | if (ack_pwrdn_i) begin | 
|  | req_pwrdn_d = 1'b0; | 
|  | state_d = FastPwrStateLowPower; | 
|  | end | 
|  | end | 
|  |  | 
|  | // Reset Path | 
|  | FastPwrStateNvmShutDown: begin | 
|  | clr_hint_o = 1'b1; | 
|  | reset_ongoing_d = 1'b1; | 
|  | state_d = FastPwrStateResetPrep; | 
|  | end | 
|  |  | 
|  | FastPwrStateResetPrep: begin | 
|  | reset_cause_d = HwReq; | 
|  | rst_lc_req_d = {PowerDomains{1'b1}}; | 
|  | rst_sys_req_d = {PowerDomains{(hw_rst_req | | 
|  | direct_rst_req | | 
|  | sw_rst_req) | | 
|  | (ndmreset_req & | 
|  | lc_ctrl_pkg::lc_tx_test_false_loose(lc_dft_en_i))}}; | 
|  |  | 
|  |  | 
|  | state_d = FastPwrStateResetWait; | 
|  | end | 
|  |  | 
|  | FastPwrStateResetWait: begin | 
|  | rst_lc_req_d = {PowerDomains{1'b1}}; | 
|  | clr_slow_req_o = reset_reqs_i[ResetMainPwrIdx]; | 
|  | // The main power reset request is checked here specifically because it is | 
|  | // the only reset request in the system that operates on the POR domain. | 
|  | // This has to be the case since it would otherwise not be able to monitor | 
|  | // the non-always-on domains. | 
|  | // | 
|  | // As a result of this, the normal reset process does not automatically | 
|  | // wipe out the reset request, so we specifically clear it and wait for it to be | 
|  | // cleared before proceeding.  This also implies if the system is under a persistent | 
|  | // glitch, or if someone just turned off the power before pwrmgr turns it off itself, | 
|  | // we will stay stuck here and perpetually hold the system in reset. | 
|  | if (reset_valid && !reset_reqs_i[ResetMainPwrIdx]) begin | 
|  | state_d = FastPwrStateLowPower; | 
|  | end | 
|  | end | 
|  |  | 
|  |  | 
|  | // Terminal state, kill everything | 
|  | // SEC_CM: FSM.TERMINAL | 
|  | default: begin | 
|  | rst_lc_req_d = {PowerDomains{1'b1}}; | 
|  | rst_sys_req_d = {PowerDomains{1'b1}}; | 
|  | ip_clk_en_d = 1'b0; | 
|  | end | 
|  | endcase // unique case (state_q) | 
|  |  | 
|  | if (fsm_invalid_i) begin | 
|  | // the slow fsm is completely out of sync, transition to terminal state | 
|  | state_d = FastPwrStateInvalid; | 
|  | end | 
|  |  | 
|  |  | 
|  | end // always_comb | 
|  |  | 
|  | assign ack_pwrup_o = ack_pwrup_q; | 
|  | assign req_pwrdn_o = req_pwrdn_q; | 
|  | assign low_power_o = low_power_q; | 
|  |  | 
|  | assign pwr_rst_o.rst_lc_req = rst_lc_req_q; | 
|  | assign pwr_rst_o.rst_sys_req = rst_sys_req_q; | 
|  | assign pwr_rst_o.reset_cause = reset_cause_q; | 
|  | assign pwr_rst_o.rstreqs = reset_reqs_i[HwResetWidth-1:0]; | 
|  |  | 
|  | // main and io clocks are only turned on/off as part of normal | 
|  | // power sequence | 
|  | assign ips_clk_en_o.main_ip_clk_en = ip_clk_en_q; | 
|  | assign ips_clk_en_o.io_ip_clk_en = ip_clk_en_q; | 
|  | prim_flop #( | 
|  | .Width(1), | 
|  | .ResetValue(1'b0) | 
|  | ) u_usb_ip_clk_en ( | 
|  | .clk_i, | 
|  | .rst_ni, | 
|  | .d_i(ip_clk_en_d & usb_ip_clk_en_i), | 
|  | .q_o(ips_clk_en_o.usb_ip_clk_en) | 
|  | ); | 
|  | assign usb_ip_clk_status_o = clk_en_status_i.usb_status; | 
|  |  | 
|  | prim_flop #( | 
|  | .Width(1), | 
|  | .ResetValue(1'b0) | 
|  | ) u_reg_otp_init ( | 
|  | .clk_i, | 
|  | .rst_ni, | 
|  | .d_i(otp_init), | 
|  | .q_o(otp_init_o) | 
|  | ); | 
|  |  | 
|  | prim_flop #( | 
|  | .Width(1), | 
|  | .ResetValue(1'b0) | 
|  | ) u_reg_lc_init ( | 
|  | .clk_i, | 
|  | .rst_ni, | 
|  | .d_i(lc_init), | 
|  | .q_o(lc_init_o) | 
|  | ); | 
|  |  | 
|  |  | 
|  | endmodule |