| /* |
| * This file is part of the MicroPython project, http://micropython.org/ |
| * |
| * Taken from ST Cube library and modified. See below for original header. |
| * |
| * The MIT License (MIT) |
| * |
| * Copyright (c) 2013, 2014 Damien P. George |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a copy |
| * of this software and associated documentation files (the "Software"), to deal |
| * in the Software without restriction, including without limitation the rights |
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| * copies of the Software, and to permit persons to whom the Software is |
| * furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| * THE SOFTWARE. |
| */ |
| |
| /** |
| ****************************************************************************** |
| * @file system_stm32.c |
| * @author MCD Application Team |
| * @version V1.0.1 |
| * @date 26-February-2014 |
| * @brief CMSIS Cortex-M4/M7 Device Peripheral Access Layer System Source File. |
| * |
| * This file provides two functions and one global variable to be called from |
| * user application: |
| * - SystemInit(): This function is called at startup just after reset and |
| * before branch to main program. This call is made inside |
| * the "startup_stm32.s" file. |
| * |
| * - SystemCoreClock variable: Contains the core clock (HCLK), it can be used |
| * by the user application to setup the SysTick |
| * timer or configure other parameters. |
| * |
| * |
| ****************************************************************************** |
| * @attention |
| * |
| * <h2><center>© COPYRIGHT 2014 STMicroelectronics</center></h2> |
| * |
| * Redistribution and use in source and binary forms, with or without modification, |
| * are permitted provided that the following conditions are met: |
| * 1. Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * 3. Neither the name of STMicroelectronics nor the names of its contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| ****************************************************************************** |
| */ |
| |
| #include "py/mphal.h" |
| #include "powerctrl.h" |
| |
| #if defined(STM32F4) || defined(STM32F7) || defined(STM32H7) || defined(STM32L4) |
| |
| void __fatal_error(const char *msg); |
| |
| /** |
| * @brief System Clock Configuration |
| * |
| * The system Clock is configured for F4/F7 as follows: |
| * (HSx should be read as HSE or HSI depending on the value of MICROPY_HW_CLK_USE_HSI) |
| * System Clock source = PLL (HSx) |
| * SYSCLK(Hz) = 168000000 |
| * HCLK(Hz) = 168000000 |
| * AHB Prescaler = 1 |
| * APB1 Prescaler = 4 |
| * APB2 Prescaler = 2 |
| * HSx Frequency(Hz) = HSx_VALUE |
| * PLL_M = HSx_VALUE/1000000 |
| * PLL_N = 336 |
| * PLL_P = 4 |
| * PLL_Q = 7 |
| * VDD(V) = 3.3 |
| * Main regulator output voltage = Scale1 mode |
| * Flash Latency(WS) = 5 |
| * |
| * The system Clock is configured for L4 as follows: |
| * System Clock source = PLL (MSI) |
| * SYSCLK(Hz) = 80000000 |
| * HCLK(Hz) = 80000000 |
| * AHB Prescaler = 1 |
| * APB1 Prescaler = 1 |
| * APB2 Prescaler = 1 |
| * MSI Frequency(Hz) = MSI_VALUE (4000000) |
| * LSE Frequency(Hz) = 32768 |
| * PLL_M = 1 |
| * PLL_N = 40 |
| * PLL_P = 7 |
| * PLL_Q = 2 |
| * PLL_R = 2 <= This is the source for SysClk, not as on F4/7 PLL_P |
| * Flash Latency(WS) = 4 |
| * @param None |
| * @retval None |
| * |
| * PLL is configured as follows: |
| * |
| * VCO_IN |
| * F4/F7 = HSx / M |
| * L4 = MSI / M |
| * VCO_OUT |
| * F4/F7 = HSx / M * N |
| * L4 = MSI / M * N |
| * PLLCLK |
| * F4/F7 = HSx / M * N / P |
| * L4 = MSI / M * N / R |
| * PLL48CK |
| * F4/F7 = HSx / M * N / Q |
| * L4 = MSI / M * N / Q USB Clock is obtained over PLLSAI1 |
| * |
| * SYSCLK = PLLCLK |
| * HCLK = SYSCLK / AHB_PRESC |
| * PCLKx = HCLK / APBx_PRESC |
| * |
| * Constraints on parameters: |
| * |
| * VCO_IN between 1MHz and 2MHz (2MHz recommended) |
| * VCO_OUT between 192MHz and 432MHz |
| * HSE = 8MHz |
| * HSI = 16MHz |
| * M = 2 .. 63 (inclusive) |
| * N = 192 ... 432 (inclusive) |
| * P = 2, 4, 6, 8 |
| * Q = 2 .. 15 (inclusive) |
| * |
| * AHB_PRESC=1,2,4,8,16,64,128,256,512 |
| * APBx_PRESC=1,2,4,8,16 |
| * |
| * Output clocks: |
| * |
| * CPU SYSCLK max 168MHz |
| * USB,RNG,SDIO PLL48CK must be 48MHz for USB |
| * AHB HCLK max 168MHz |
| * APB1 PCLK1 max 42MHz |
| * APB2 PCLK2 max 84MHz |
| * |
| * Timers run from APBx if APBx_PRESC=1, else 2x APBx |
| */ |
| void SystemClock_Config(void) { |
| #if defined(STM32F7) |
| // The DFU bootloader changes the clocksource register from its default power |
| // on reset value, so we set it back here, so the clocksources are the same |
| // whether we were started from DFU or from a power on reset. |
| RCC->DCKCFGR2 = 0; |
| #endif |
| |
| RCC_ClkInitTypeDef RCC_ClkInitStruct; |
| RCC_OscInitTypeDef RCC_OscInitStruct; |
| #if defined(STM32H7) |
| RCC_PeriphCLKInitTypeDef PeriphClkInitStruct; |
| #endif |
| |
| #if defined(STM32F4) || defined(STM32F7) || defined(STM32H7) |
| |
| /* Enable Power Control clock */ |
| #if defined(STM32H7) |
| MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0); |
| #else |
| __PWR_CLK_ENABLE(); |
| #endif |
| |
| /* The voltage scaling allows optimizing the power consumption when the device is |
| clocked below the maximum system frequency, to update the voltage scaling value |
| regarding system frequency refer to product datasheet. */ |
| __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); |
| #elif defined(STM32L4) |
| // Configure LSE Drive Capability |
| __HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_LOW); |
| #endif |
| |
| #if defined(STM32H7) |
| // Wait for PWR_FLAG_VOSRDY |
| while ((PWR->D3CR & (PWR_D3CR_VOSRDY)) != PWR_D3CR_VOSRDY) { |
| } |
| #endif |
| |
| /* Enable HSE Oscillator and activate PLL with HSE as source */ |
| #if defined(STM32F4) || defined(STM32F7) || defined(STM32H7) |
| RCC_OscInitStruct.OscillatorType = MICROPY_HW_RCC_OSCILLATOR_TYPE; |
| RCC_OscInitStruct.HSEState = MICROPY_HW_RCC_HSE_STATE; |
| RCC_OscInitStruct.HSIState = MICROPY_HW_RCC_HSI_STATE; |
| RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; |
| #if defined(STM32H7) |
| RCC_OscInitStruct.CSIState = RCC_CSI_OFF; |
| #endif |
| RCC_OscInitStruct.PLL.PLLSource = MICROPY_HW_RCC_PLL_SRC; |
| #elif defined(STM32L4) |
| |
| #if MICROPY_HW_CLK_USE_HSE |
| RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; |
| RCC_OscInitStruct.HSEState = RCC_HSE_ON; |
| RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; |
| RCC_OscInitStruct.PLL.PLLM = MICROPY_HW_CLK_PLLM; |
| RCC_OscInitStruct.PLL.PLLN = MICROPY_HW_CLK_PLLN; |
| RCC_OscInitStruct.PLL.PLLP = MICROPY_HW_CLK_PLLP; |
| RCC_OscInitStruct.PLL.PLLQ = MICROPY_HW_CLK_PLLQ; |
| RCC_OscInitStruct.PLL.PLLR = MICROPY_HW_CLK_PLLR; |
| RCC_OscInitStruct.MSIState = RCC_MSI_OFF; |
| #else |
| RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE | RCC_OSCILLATORTYPE_MSI; |
| RCC_OscInitStruct.MSIState = RCC_MSI_ON; |
| RCC_OscInitStruct.MSICalibrationValue = RCC_MSICALIBRATION_DEFAULT; |
| RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_6; |
| RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_MSI; |
| #endif |
| |
| #if MICROPY_HW_RTC_USE_LSE |
| RCC_OscInitStruct.LSEState = RCC_LSE_ON; |
| #else |
| RCC_OscInitStruct.LSEState = RCC_LSE_OFF; |
| #endif |
| |
| #endif |
| RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; |
| /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2 |
| clocks dividers */ |
| RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2); |
| #if defined(STM32H7) |
| RCC_ClkInitStruct.ClockType |= (RCC_CLOCKTYPE_D3PCLK1 | RCC_CLOCKTYPE_D1PCLK1); |
| #endif |
| |
| #if defined(MICROPY_HW_CLK_LAST_FREQ) && MICROPY_HW_CLK_LAST_FREQ |
| #if defined(STM32F7) |
| #define FREQ_BKP BKP31R |
| #elif defined(STM32L4) |
| #error Unsupported Processor |
| #else |
| #define FREQ_BKP BKP19R |
| #endif |
| uint32_t m = RTC->FREQ_BKP; |
| uint32_t n; |
| uint32_t p; |
| uint32_t q; |
| |
| // 222111HH HHQQQQPP nNNNNNNN NNMMMMMM |
| uint32_t h = (m >> 22) & 0xf; |
| uint32_t b1 = (m >> 26) & 0x7; |
| uint32_t b2 = (m >> 29) & 0x7; |
| q = (m >> 18) & 0xf; |
| p = (((m >> 16) & 0x03) + 1) * 2; |
| n = (m >> 6) & 0x3ff; |
| m &= 0x3f; |
| if ((q < 2) || (q > 15) || (p > 8) || (p < 2) || (n < 192) || (n >= 433) || (m < 2)) { |
| m = MICROPY_HW_CLK_PLLM; |
| n = MICROPY_HW_CLK_PLLN; |
| p = MICROPY_HW_CLK_PLLP; |
| q = MICROPY_HW_CLK_PLLQ; |
| h = RCC_SYSCLK_DIV1; |
| b1 = RCC_HCLK_DIV4; |
| b2 = RCC_HCLK_DIV2; |
| } else { |
| h <<= 4; |
| b1 <<= 10; |
| b2 <<= 10; |
| } |
| RCC_OscInitStruct.PLL.PLLM = m; //MICROPY_HW_CLK_PLLM; |
| RCC_OscInitStruct.PLL.PLLN = n; //MICROPY_HW_CLK_PLLN; |
| RCC_OscInitStruct.PLL.PLLP = p; //MICROPY_HW_CLK_PLLP; |
| RCC_OscInitStruct.PLL.PLLQ = q; //MICROPY_HW_CLK_PLLQ; |
| |
| RCC_ClkInitStruct.AHBCLKDivider = h; //RCC_SYSCLK_DIV1; |
| RCC_ClkInitStruct.APB1CLKDivider = b1; //RCC_HCLK_DIV4; |
| RCC_ClkInitStruct.APB2CLKDivider = b2; //RCC_HCLK_DIV2; |
| #else // defined(MICROPY_HW_CLK_LAST_FREQ) && MICROPY_HW_CLK_LAST_FREQ |
| RCC_OscInitStruct.PLL.PLLM = MICROPY_HW_CLK_PLLM; |
| RCC_OscInitStruct.PLL.PLLN = MICROPY_HW_CLK_PLLN; |
| RCC_OscInitStruct.PLL.PLLP = MICROPY_HW_CLK_PLLP; |
| RCC_OscInitStruct.PLL.PLLQ = MICROPY_HW_CLK_PLLQ; |
| #if defined(STM32L4) || defined(STM32H7) |
| RCC_OscInitStruct.PLL.PLLR = MICROPY_HW_CLK_PLLR; |
| #endif |
| |
| #if defined(STM32H7) |
| RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_1; |
| RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE; |
| RCC_OscInitStruct.PLL.PLLFRACN = 0; |
| #endif |
| |
| #if defined(STM32F4) || defined(STM32F7) |
| RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; |
| RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; |
| RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; |
| #elif defined(STM32L4) |
| RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; |
| RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; |
| RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; |
| #elif defined(STM32H7) |
| RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1; |
| RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2; |
| RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2; |
| RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2; |
| RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; |
| RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; |
| #endif |
| #endif |
| |
| if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { |
| __fatal_error("HAL_RCC_OscConfig"); |
| } |
| |
| #if defined(STM32H7) |
| /* PLL3 for USB Clock */ |
| PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USB; |
| PeriphClkInitStruct.UsbClockSelection = RCC_USBCLKSOURCE_PLL3; |
| PeriphClkInitStruct.PLL3.PLL3M = MICROPY_HW_CLK_PLL3M; |
| PeriphClkInitStruct.PLL3.PLL3N = MICROPY_HW_CLK_PLL3N; |
| PeriphClkInitStruct.PLL3.PLL3P = MICROPY_HW_CLK_PLL3P; |
| PeriphClkInitStruct.PLL3.PLL3Q = MICROPY_HW_CLK_PLL3Q; |
| PeriphClkInitStruct.PLL3.PLL3R = MICROPY_HW_CLK_PLL3R; |
| PeriphClkInitStruct.PLL3.PLL3RGE = RCC_PLL3VCIRANGE_1; |
| PeriphClkInitStruct.PLL3.PLL3VCOSEL = RCC_PLL3VCOWIDE; |
| PeriphClkInitStruct.PLL3.PLL3FRACN = 0; |
| if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) { |
| __fatal_error("HAL_RCCEx_PeriphCLKConfig"); |
| } |
| #endif |
| |
| #if defined(STM32F7) |
| /* Activate the OverDrive to reach the 200 MHz Frequency */ |
| if (HAL_PWREx_EnableOverDrive() != HAL_OK) { |
| __fatal_error("HAL_PWREx_EnableOverDrive"); |
| } |
| #endif |
| |
| uint32_t vco_out = RCC_OscInitStruct.PLL.PLLN * (MICROPY_HW_CLK_VALUE / 1000000) / RCC_OscInitStruct.PLL.PLLM; |
| uint32_t sysclk_mhz = vco_out / RCC_OscInitStruct.PLL.PLLP; |
| bool need_pllsai = vco_out % 48 != 0; |
| if (powerctrl_rcc_clock_config_pll(&RCC_ClkInitStruct, sysclk_mhz, need_pllsai) != 0) { |
| __fatal_error("HAL_RCC_ClockConfig"); |
| } |
| |
| #if defined(STM32H7) |
| /* Activate CSI clock mandatory for I/O Compensation Cell*/ |
| __HAL_RCC_CSI_ENABLE(); |
| |
| /* Enable SYSCFG clock mandatory for I/O Compensation Cell */ |
| __HAL_RCC_SYSCFG_CLK_ENABLE(); |
| |
| /* Enable the I/O Compensation Cell */ |
| HAL_EnableCompensationCell(); |
| |
| /* Enable the USB voltage level detector */ |
| HAL_PWREx_EnableUSBVoltageDetector(); |
| #endif |
| |
| #if defined(STM32L4) |
| // Enable MSI-Hardware auto calibration mode with LSE |
| HAL_RCCEx_EnableMSIPLLMode(); |
| |
| RCC_PeriphCLKInitTypeDef PeriphClkInitStruct; |
| PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SAI1 | RCC_PERIPHCLK_I2C1 |
| | RCC_PERIPHCLK_USB | RCC_PERIPHCLK_ADC |
| | RCC_PERIPHCLK_RNG | RCC_PERIPHCLK_RTC; |
| PeriphClkInitStruct.I2c1ClockSelection = RCC_I2C1CLKSOURCE_PCLK1; |
| |
| PeriphClkInitStruct.Sai1ClockSelection = RCC_SAI1CLKSOURCE_PLLSAI1; |
| PeriphClkInitStruct.AdcClockSelection = RCC_ADCCLKSOURCE_PLLSAI1; |
| PeriphClkInitStruct.UsbClockSelection = RCC_USBCLKSOURCE_PLLSAI1; |
| PeriphClkInitStruct.RngClockSelection = RCC_RNGCLKSOURCE_PLLSAI1; |
| |
| #if MICROPY_HW_RTC_USE_LSE |
| PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE; |
| #else |
| PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSI; |
| #endif |
| |
| #if MICROPY_HW_CLK_USE_HSE |
| PeriphClkInitStruct.PLLSAI1.PLLSAI1Source = RCC_PLLSOURCE_HSE; |
| PeriphClkInitStruct.PLLSAI1.PLLSAI1M = 1; //MICROPY_HW_CLK_PLLSAIM; |
| PeriphClkInitStruct.PLLSAI1.PLLSAI1N = MICROPY_HW_CLK_PLLSAIN; |
| PeriphClkInitStruct.PLLSAI1.PLLSAI1P = MICROPY_HW_CLK_PLLSAIP; |
| PeriphClkInitStruct.PLLSAI1.PLLSAI1Q = MICROPY_HW_CLK_PLLSAIQ; |
| PeriphClkInitStruct.PLLSAI1.PLLSAI1R = MICROPY_HW_CLK_PLLSAIR; |
| #else |
| /* PLLSAI is used to clock USB, ADC, I2C1 and RNG. The frequency is |
| MSI(4MHz)/PLLM(1)*PLLSAI1N(24)/PLLSAIQ(2) = 48MHz. See the STM32CubeMx |
| application or the reference manual. */ |
| PeriphClkInitStruct.PLLSAI1.PLLSAI1Source = RCC_PLLSOURCE_MSI; |
| PeriphClkInitStruct.PLLSAI1.PLLSAI1M = 1; |
| PeriphClkInitStruct.PLLSAI1.PLLSAI1N = 24; |
| PeriphClkInitStruct.PLLSAI1.PLLSAI1P = RCC_PLLP_DIV7; |
| PeriphClkInitStruct.PLLSAI1.PLLSAI1Q = RCC_PLLQ_DIV2; |
| PeriphClkInitStruct.PLLSAI1.PLLSAI1R = RCC_PLLR_DIV2; |
| #endif |
| PeriphClkInitStruct.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_SAI1CLK |
| | RCC_PLLSAI1_48M2CLK |
| | RCC_PLLSAI1_ADC1CLK; |
| |
| if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) { |
| __fatal_error("HAL_RCCEx_PeriphCLKConfig"); |
| } |
| |
| __PWR_CLK_ENABLE(); |
| |
| HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1); |
| |
| HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / 1000); |
| HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK); |
| NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, TICK_INT_PRIORITY, 0)); |
| #endif |
| } |
| |
| #endif |