/* Cesar project {{{ * * Copyright (C) 2008 Spidcom * * <<>> * * }}} */ /** * \file hal/phy/src/phy.c * \brief HAL Phy general functions. * \ingroup hal_phy */ #include "common/std.h" #include "hal/phy/phy.h" #include "inc/context.h" #include "inc/regs.h" #include "inc/resys.h" #include "hal/phy/spoc/spoc.h" #include "hal/leon/itc2.h" #include "hal/arch/arch.h" #include #include "hal_phy_resys_labels.h" #include "hal_phy_params.h" #include "config/phy/trace/more.h" #include "config/phy/cap.h" #include "config/phy/cap/mask.h" #include "lib/stats.h" #include "lib/fixed.h" #include /** Interesting interrupts for the PHY. */ #define PHY_ITC2_MASK (BITS_MASK (1, LEON_ITC2_IT__RESYS) \ | BITS_MASK (1, LEON_ITC2_IT__PRATIC_ACCESS_CONF) \ | BITS_MASK (1, LEON_ITC2_IT__PRATIC_ACCESS) \ | BITS_MASK (1, LEON_ITC2_IT__PRATIC_INTERRUPT) \ | BITS_MASK (1, LEON_ITC2_IT__PB_DMA_END) \ | (!CONFIG_PHY_TRACE_MORE ? 0 : \ BITS_MASK (1, LEON_ITC2_IT__MAPPOWSE_END_TX_FRAME) \ | BITS_MASK (1, LEON_ITC2_IT__PB_DMA_ERROR) \ | BITS_MASK (1, LEON_ITC2_IT__CONGESTION_TX_ERROR))) /** Length of SYNCP portion of the Preamble (+1 for hardware detection * delay). */ #define PHY_PREAMBLE_SYNCP_TCK ((192 + 8 * 384) / 3) /** Interruption latency considered too large. */ #define PHY_INTERRUPT_DELAY_MAX_TCK (25 * 30) /** Address below which RESYS is considered not started. */ #define PHY_RESYS_NOT_STARTED_ADDRESS 4 /** RegBank clock command DSP. */ #define PHY_RB_CLK_CMD_DSP (*(volatile u32 *) 0xC8040470) /** RegBank clock command AFE. */ #define PHY_RB_CLK_CMD_AFE (*(volatile u32 *) 0xC8040480) /** RegBank reset register. */ #define PHY_RB_RST_GROUP (*(volatile u32 *) 0xC8040708) /** DSP reset in PHY_RB_RST_GROUP. */ #define PHY_RB_RST_GROUP__DSP 2, 2 /** TM DMA descriptor. */ struct phy_tmdma_desc_t { /** Pointer to next descriptor. */ struct phy_tmdma_desc_t *next; /** Pointer to data. */ u32 *data; BITFIELDS_WORD ( /** Transfer configuration: size. */ u32 size_words:8;, /** Transfer configuration: memory index. */ u32 mem_index:3;, /** Transfer configuration: last. */ u32 last:1;, u32 :4;, /** Transfer configuration: local start address in memory. */ u32 local_start_addr:16;) }; typedef struct phy_tmdma_desc_t phy_tmdma_desc_t; /** TM DMA memory indexes. */ enum phy_tmdma_mem_index_t { PHY_TMDMA_MEM_TONEMASK = 0, PHY_TMDMA_MEM_AMPLITUDE_MAP = 1, PHY_TMDMA_MEM_ADAPT_TABLE = 2, PHY_TMDMA_MEM_TONEMAP_0 = 3, PHY_TMDMA_MEM_TONEMAP_1 = 4, }; /** Tunable parameters. */ struct phy_tunable_t { /** MAFADESE filters, band 0. */ int mafadese_coef_filter_band0[26]; /** MAFADESE filters, band 1. */ int mafadese_coef_filter_band1[26]; /** SPOC initial setting. */ s32 spoc_rho_initial_q30; /** Internal delta accumulation coefficient. */ uint delta_res_coef_internal; /** External delta accumulation coefficient. */ uint delta_res_coef_external; }; typedef struct phy_tunable_t phy_tunable_t; #if DEBUG /** Debug tools. */ struct phy_debug_t { /** Recorded date at fatal error. */ u32 fatal_date; /** Last used tonemap. */ blk_t *last_tonemap; }; typedef struct phy_debug_t phy_debug_t; #endif /* DEBUG */ /** Global Phy context. */ static phy_t ARCH_DLRAM_BSS phy_global; /** Global Phy tunable parameters. */ static phy_tunable_t phy_tunable = { PHY_PARAM_MAFADESE_COEF_FILTER_BAND0, PHY_PARAM_MAFADESE_COEF_FILTER_BAND1, 0, PHY_PARAM_CHANNEL_ESTIM_COEF__COEF_RES_DELTA_INTERNAL, PHY_PARAM_CHANNEL_ESTIM_COEF__COEF_RES_DELTA_EXTERNAL, }; #if DEBUG /** Global debug tools. */ static phy_debug_t phy_debug; #endif /* DEBUG */ /** Specific Phy VSR. */ extern void phy_vsr (void); /** Data used by the specific VSR. */ extern cyg_addrword_t phy_interrupt_data; /** * Interrupt handler. * \param vector interrupt vector number * \param data phy context * \return status */ cyg_uint32 ARCH_ILRAM phy_isr (cyg_vector_t vector, cyg_addrword_t data) { phy_t *ctx = (phy_t *) data; dbg_claim (ctx); bool call_dsr; dbg_claim (BF_GET (LEON_ITC2_STATUS_HIGH__IP, LEON_ITC2_STATUS_HIGH)); uint it = BF_GET (LEON_ITC2_STATUS_HIGH__IRL, LEON_ITC2_STATUS_HIGH); /* Keep/reset watchdog state. */ bool watchdog_expired = !(PHY_PRATIC_TIMER_7_CTRL & BF_MASK (PHY_PRATIC_TIMER_X_CTRL__VALID)); if (!(PHY_PRATIC_TIMER_1_CTRL & BF_MASK (PHY_PRATIC_TIMER_X_CTRL__VALID))) PHY_PRATIC_TIMER_7_CTRL = 0; /* Acknowledge right now to detect all PBDMA interrupts. */ LEON_ITC2_CLEAR = 1 << it; LEON_ITC1_CLEAR = 1 << LEON_ITC2_HIGH_PRIORITY_ITC1_IT; if (it == LEON_ITC2_IT__RESYS) { if (ctx->pbdma_start_on_resys_it) { PHY_TRACE (PBDMA_START_ON_RESYS, phy_date ()); /* Start PBDMA. */ PHY_PRATIC_IMMEDIATE_ACTION = PHY_PRATIC_ACTION__PBD_START; /* Clear sound flag (see maria:#703). */ PHY_DSPSS_RX_PARAM = PHY_DSPSS_RX_PARAM__DEFAULT | BF_FILL (PHY_DSPSS_RX_PARAM, (FC_MODE, ctx->rx_fc_mode)); /* Do not do it again. */ ctx->pbdma_start_on_resys_it = false; call_dsr = false; } else { /* Workaround for maria:#860. */ PHY_PRATIC_IMMEDIATE_ACTION = PHY_PRATIC_ACTION__CREATE_FC_10; /* Wait FC decoding done. */ u32 status = PHY_DSPSS_RX_FC_AV_STATUS; while (!(status & BF_MASK (PHY_DSPSS_RX_FC_AV_STATUS__RECEIVED))) status = PHY_DSPSS_RX_FC_AV_STATUS; /* Clear for next reception. */ PHY_DSPSS_RX_FC_AV_STATUS = 0; /* Get preamble date and CRC result. */ u32 offset_smp = BF_GET (PHY_DSPSS_RESYS_DETECT_OFFSET__PREAMBLE, PHY_DSPSS_RESYS_DETECT_OFFSET); u32 rx_date = PHY_PRATIC_STA_LAST_RECEIVED_FRAME_DATE - offset_smp / 3 - PHY_PREAMBLE_SYNCP_TCK - PHY_RX_HARDWARE_DELAY_TCK; PHY_TRACE (RX_FC_CB, phy_date (), rx_date); if (status & BF_MASK (PHY_DSPSS_RX_FC_AV_STATUS__CRC_OK)) { u32 fc_av[4]; fc_av[0] = PHY_DSPSS_RX_FC_AV_0; fc_av[1] = PHY_DSPSS_RX_FC_AV_1; fc_av[2] = PHY_DSPSS_RX_FC_AV_2; fc_av[3] = PHY_DSPSS_RX_FC_AV_3; call_dsr = ctx->rx_fc_cb (ctx->user_data, rx_date, fc_av); } else call_dsr = ctx->rx_fc_cb (ctx->user_data, rx_date, NULL); } } else if (it == LEON_ITC2_IT__PB_DMA_END) { u32 status_word = PHY_PBDMA_STATUS_ERROR; PHY_TRACE (PBDMA_CB, phy_date (), status_word); call_dsr = ctx->pbdma_cb (ctx->user_data, status_word); } else if (it == LEON_ITC2_IT__PRATIC_ACCESS) { trace_do (u32 eff_date = PHY_PRATIC_TIMER_1_EFFECTIVE_DATE;) PHY_TRACE (ACCESS_CB, phy_date (), eff_date); if (watchdog_expired) { ctx->stats.watchdog_expired++; uint resys_exe = PHY_DSPSS_RESYS_EXE_INSTR_ADDR; PHY_TRACE (WATCHDOG_EXPIRED, PHY_PRATIC_STA_LAST_RECEIVED_FRAME_DATE, resys_exe, PHY_PRATIC_CSMA); /* If RESYS is not started, the preamble detection which stopped * ACCESS timer did not start RESYS. Restart it to put PRATIC in * IDLE state. */ if (resys_exe <= PHY_RESYS_NOT_STARTED_ADDRESS) { ctx->stats.resys_restarted++; PHY_DSPSS_RESYS_PARAM = BF_FILL (PHY_DSPSS_RESYS_PARAM, (RESYS_COND, 0), (RESYS_ON, 0)); PHY_DSPSS_RESYS_PARAM = BF_FILL (PHY_DSPSS_RESYS_PARAM, (RESYS_COND, 0), (RESYS_ON, 1)); } else dbg_fatal ("hardware freeze, no ACCESS, no RESYS"); } else PHY_TRACE_DATE_WARNING (eff_date + PHY_INTERRUPT_DELAY_MAX_TCK); call_dsr = ctx->access_cb (ctx->user_data); /* Workaround for maria:957: if hardware has detected a preamble, * cancel any pending TX. */ uint resys_exe = PHY_DSPSS_RESYS_EXE_INSTR_ADDR; if (resys_exe > PHY_RESYS_LABEL_WAIT_PRE && resys_exe <= PHY_RESYS_LABEL_PREAMBLE_ESTIM_END) phy_tx_cancel (ctx); } else if (it == LEON_ITC2_IT__PRATIC_ACCESS_CONF) { trace_do (u32 eff_date = PHY_PRATIC_TIMER_2_EFFECTIVE_DATE;) PHY_TRACE (ACCESS_CONF_CB, phy_date (), eff_date); PHY_TRACE_DATE_WARNING (eff_date + PHY_INTERRUPT_DELAY_MAX_TCK); call_dsr = ctx->access_conf_cb (ctx->user_data); } else if (it == LEON_ITC2_IT__PRATIC_INTERRUPT) { trace_do (u32 eff_date = PHY_PRATIC_TIMER_5_EFFECTIVE_DATE;) PHY_TRACE (EXTRA_TIMER_CB, phy_date (), eff_date); PHY_TRACE_DATE_WARNING (eff_date + PHY_INTERRUPT_DELAY_MAX_TCK); call_dsr = ctx->extra_timer_cb (ctx->user_data); } else if (CONFIG_PHY_TRACE_MORE && it == LEON_ITC2_IT__MAPPOWSE_END_TX_FRAME) { PHY_TRACE (TX_END, phy_date ()); call_dsr = false; } else if (CONFIG_PHY_TRACE_MORE && it == LEON_ITC2_IT__PB_DMA_ERROR) { PHY_TRACE (PBDMA_ERROR, phy_date (), PHY_PBDMA_STATUS_ERROR); call_dsr = false; } else if (CONFIG_PHY_TRACE_MORE && it == LEON_ITC2_IT__CONGESTION_TX_ERROR) { PHY_TRACE (TX_CONGESTION, phy_date ()); call_dsr = false; } else { dbg_assert_default (); } PHY_TRACE (ISR_EXIT, phy_date ()); return (call_dsr ? CYG_ISR_CALL_DSR : 0) | CYG_ISR_HANDLED; } /** * DSR handler. * \param vector interrupt vector number * \param count number of time DSR was requested * \param data phy context * \return status */ static void ARCH_ILRAM phy_dsr (cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data) { phy_t *ctx = (phy_t *) data; dbg_claim (ctx); PHY_TRACE (DEFERRED_CB, phy_date ()); ctx->deferred_cb (ctx->user_data); } #if DEBUG /** * Record urgent registers for fatal dump. */ static void phy_fatal (void) { phy_debug.fatal_date = phy_date (); } /** * Dump interesting parameters on fatal error. */ static void phy_fatal_dump (dbg_dump_callback_t dump_cb, void *dump_cb_user) { u32 resys_debug1 = PHY_DSPSS_RESYS_DEBUG_1; dbg_dump_printf ( dump_cb, dump_cb_user, "date: 0x%08x\n", phy_debug.fatal_date); dbg_dump_printf ( dump_cb, dump_cb_user, "last preamble: 0x%08x - %d / 3 - %d - %d\n", PHY_PRATIC_STA_LAST_RECEIVED_FRAME_DATE, BF_GET (PHY_DSPSS_RESYS_DETECT_OFFSET__PREAMBLE, PHY_DSPSS_RESYS_DETECT_OFFSET), PHY_PREAMBLE_SYNCP_TCK, PHY_RX_HARDWARE_DELAY_TCK); dbg_dump_printf ( dump_cb, dump_cb_user, "RESYS: exe: 0x%03x symbol: %d sample: " " %d trans: %d error fft: %d soft: 0x%08x\n", PHY_DSPSS_RESYS_EXE_INSTR_ADDR, BF_GET (PHY_DSPSS_RESYS_DEBUG_1__SYMBOL_COUNT, resys_debug1), BF_GET (PHY_DSPSS_RESYS_DEBUG_1__SAMPLE_COUNT, resys_debug1), BF_GET (PHY_DSPSS_RESYS_DEBUG_2__NEXT_SYMBOL_TRANSITION, PHY_DSPSS_RESYS_DEBUG_2), PHY_DSPSS_RESYS_ERROR_FFT_NUMBER, PHY_DSPSS_RESYS_TIME_TO_RISE_SOFTCOND); dbg_dump_printf ( dump_cb, dump_cb_user, "PB chain: 0x%08x\nTX chain: 0x%08x\nRX chain: 0x%08x\n", PHY_DSPSS_PB_CHAIN_INFO, PHY_DSPSS_TX_CHAIN_INFO, PHY_DSPSS_RX_CHAIN_INFO); dbg_dump_printf ( dump_cb, dump_cb_user, "time chain: in progress: %d\n", BF_GET (PHY_DSPSS_TIME_CHAIN_INFO__CREATION_IN_PROGRESS, PHY_DSPSS_TIME_CHAIN_INFO)); u32 csma = PHY_PRATIC_CSMA; char flag[8], *flagp = flag; if (csma & BF_MASK (PHY_PRATIC_CSMA__FALSE_ALARM)) *flagp++ = 'f'; if (csma & BF_MASK (PHY_PRATIC_CSMA__TX_PRIORITY_LOST)) *flagp++ = 'P'; if (csma & BF_MASK (PHY_PRATIC_CSMA__TX_WHILE_RX)) *flagp++ = 'R'; if (csma & BF_MASK (PHY_PRATIC_CSMA__TX_CANCEL_DUE_TO_FALSE_ALARM)) *flagp++ = 'F'; *flagp = '\0'; dbg_dump_printf ( dump_cb, dump_cb_user, "CSMA: cap: %us%um%u%s\n", BF_GET (PHY_PRATIC_CSMA__MPDU_CAP, csma), BF_GET (PHY_PRATIC_CSMA__SENT_CAP, csma), BF_GET (PHY_PRATIC_CSMA__MEDIUM_CAP, csma), flag); dbg_dump_printf ( dump_cb, dump_cb_user, "TMDMA: busy: %d\n", BF_GET (PHY_DSPSS_TMD_CTRL__BUSY, PHY_DSPSS_TMD_CTRL)); u32 pbdma_ctrl = PHY_PBDMA_CTRL_CONFIG; u32 pbdma_status = PHY_PBDMA_STATUS_ERROR; dbg_dump_printf ( dump_cb, dump_cb_user, "PBDMA: data: %d chandata: %d pb null: %d" " index: %d fsm: %d it: %d end rx: %d end tx: %d end chandata: %d\n", BF_GET (PHY_PBDMA_CTRL_CONFIG__START_DATA, pbdma_ctrl), BF_GET (PHY_PBDMA_CTRL_CONFIG__START_CHANDATA, pbdma_ctrl), BF_GET (PHY_PBDMA_STATUS_ERROR__PB_NULL, pbdma_status), BF_GET (PHY_PBDMA_STATUS_ERROR__CURRENT_PB_INDEX, pbdma_status), BF_GET (PHY_PBDMA_STATUS_ERROR__FSM_STATE, pbdma_status), BF_GET (PHY_PBDMA_STATUS_ERROR__PB_IT, pbdma_status), BF_GET (PHY_PBDMA_STATUS_ERROR__END_RX_PB, pbdma_status), BF_GET (PHY_PBDMA_STATUS_ERROR__END_TX_PB, pbdma_status), BF_GET (PHY_PBDMA_STATUS_ERROR__END_CHANDATA, pbdma_status)); dbg_dump_printf ( dump_cb, dump_cb_user, "PBDMA error: rx_header_load: %d" " ahb_response: %d chandata type: %d chandata size: %d\n", BF_GET (PHY_PBDMA_STATUS_ERROR__RX_HEADER_LOAD_ERROR, pbdma_status), BF_GET (PHY_PBDMA_STATUS_ERROR__AHB_RESPONSE_ERROR, pbdma_status), BF_GET (PHY_PBDMA_STATUS_ERROR__CHANDATA_TYPE_FORBIDDEN, pbdma_status), BF_GET (PHY_PBDMA_STATUS_ERROR__CHANDATA_SIZE_FORBIDDEN, pbdma_status)); /* Dump last used tonemap. */ blk_t *last_tonemap = phy_debug.last_tonemap; if (last_tonemap && last_tonemap != INVALID_PTR && last_tonemap->next && last_tonemap->next != INVALID_PTR) { dbg_dump_printf (dump_cb, dump_cb_user, "last tonemap: 0x%08x\n ", (u32) last_tonemap); const u32 *p = (u32 *) last_tonemap->data; uint i; for (i = 0; i < PHY_TONEMAP_WORDS; i++) { if (i == BLK_SIZE / sizeof (u32)) p = (u32 *) last_tonemap->next->data; dbg_dump_printf (dump_cb, dump_cb_user, "0x%08x%s", *p++, i == PHY_TONEMAP_WORDS - 1 ? "\n" : ((i + 1) % 8 ? ", " : ",\n ")); } } } #endif /* DEBUG */ static void phy_init_dsp (void) { /* Activate clocks and release DSP reset. */ PHY_RB_CLK_CMD_AFE = 1; PHY_RB_CLK_CMD_DSP = 1; PHY_RB_RST_GROUP &= ~BF_MASK (PHY_RB_RST_GROUP__DSP); } static void phy_init_tunable_param (phy_t *ctx) { #if CONFIG_STATS /* Identifiers needs to be statically allocated, cannot loop. */ #define REGISTER_TUNABLE_MAFADESE_FILTER(band, index) \ lib_stats_set_stat_value_notype ( \ "MAFADESE_COEF_FILTER_BAND" #band "_" #index, \ &phy_tunable.mafadese_coef_filter_band ## band[index], \ LIB_STATS_ACCESS_WRITE_ONLY, LIB_STATS_DEBUG); PREPROC_FOR_EACH_PARAM (REGISTER_TUNABLE_MAFADESE_FILTER, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25); PREPROC_FOR_EACH_PARAM (REGISTER_TUNABLE_MAFADESE_FILTER, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25); #undef REGISTER_TUNABLE_MAFADESE_FILTER lib_stats_set_stat_value_notype ("SPOC_RHO_INITIAL_Q30", &phy_tunable.spoc_rho_initial_q30, LIB_STATS_ACCESS_WRITE_ONLY, LIB_STATS_DEBUG); lib_stats_set_stat_value_notype ("DELTA_RES_COEF_INTERNAL", &phy_tunable.delta_res_coef_internal, LIB_STATS_ACCESS_WRITE_ONLY, LIB_STATS_DEBUG); lib_stats_set_stat_value_notype ("DELTA_RES_COEF_EXTERNAL", &phy_tunable.delta_res_coef_external, LIB_STATS_ACCESS_WRITE_ONLY, LIB_STATS_DEBUG); lib_stats_set_stat_value_notype ("CAP_MASK", &ctx->cap_mask, LIB_STATS_ACCESS_WRITE_ONLY, LIB_STATS_DEBUG); lib_stats_set_stat_value_notype ("DELTA_RES_COEF_INTERNAL", &phy_tunable.delta_res_coef_internal, LIB_STATS_ACCESS_WRITE_ONLY, LIB_STATS_DEBUG); lib_stats_set_stat_value_notype ("DELTA_RES_COEF_EXTERNAL", &phy_tunable.delta_res_coef_external, LIB_STATS_ACCESS_WRITE_ONLY, LIB_STATS_DEBUG); #endif ctx->cap_mask = CONFIG_PHY_CAP_MASK_DEFAULT; } static void phy_init_stats (phy_t *ctx) { memset (&ctx->stats, 0, sizeof (ctx->stats)); #define PHY_STAT(s) \ lib_stats_set_stat_value_notype ("phy_" #s, &ctx->stats.s, \ LIB_STATS_ACCESS_READ_ONLY, \ LIB_STATS_DEBUG) PHY_STAT (watchdog_expired); PHY_STAT (resys_restarted); #undef PHY_STAT } phy_t * phy_init (void *user_data, phy_rx_fc_cb_t rx_fc_cb, phy_access_cb_t access_cb, phy_access_conf_cb_t access_conf_cb, phy_pbdma_cb_t pbdma_cb, phy_tx_false_alarm_cb_t tx_false_alarm_cb, phy_deferred_cb_t deferred_cb) { /* Initialise context. */ phy_t *ctx = &phy_global; phy_trace_init (ctx); PHY_TRACE (INIT); ctx->user_data = user_data; ctx->rx_fc_cb = rx_fc_cb; ctx->access_cb = access_cb; ctx->access_conf_cb = access_conf_cb; ctx->pbdma_cb = pbdma_cb; ctx->tx_false_alarm_cb = tx_false_alarm_cb; ctx->deferred_cb = deferred_cb; ctx->extra_timer_user_data = NULL; ctx->extra_timer_cb = NULL; ctx->rx_fc_mode = PHY_FC_MODE_NONE; ctx->tx_fc_mode = PHY_FC_MODE_NONE; ctx->gil_durations_table[PHY_GIL_417] = PHY_DSPSS_TX_GUARD_LENGTH__VALUE_417; ctx->gil_durations_table[PHY_GIL_567] = PHY_DSPSS_TX_GUARD_LENGTH__VALUE_567; ctx->gil_durations_table[PHY_GIL_3534] = PHY_DSPSS_TX_GUARD_LENGTH__VALUE_3534; ctx->fc_symbol_nb[PHY_FC_MODE_HYBRID_1] = 4 + 1; ctx->fc_symbol_nb[PHY_FC_MODE_HYBRID_2] = 4 + 2; ctx->fc_symbol_nb[PHY_FC_MODE_AV_1] = 1; ctx->fc_symbol_nb[PHY_FC_MODE_AV_2] = 2; ctx->resys_gil_table[PHY_GIL_417] = PHY_RESYS_GIL_OFFSET__VALUE_417; ctx->resys_gil_table[PHY_GIL_567] = PHY_RESYS_GIL_OFFSET__VALUE_567; ctx->resys_gil_table[PHY_GIL_3534] = PHY_RESYS_GIL_OFFSET__VALUE_3534; ctx->pbdma_start_on_resys_it = false; dbg_do (dbg_register_fatal_callback (1, phy_fatal)); dbg_do (dbg_register_fatal_dump_callback (1, phy_fatal_dump)); /* Attach interrupt. */ cyg_drv_interrupt_create (LEON_ITC2_HIGH_PRIORITY_ITC1_IT, LEON_ITC2_HIGH_PRIORITY_ITC1_IT_PRIORITY, (cyg_addrword_t) ctx, phy_isr, phy_dsr, &ctx->interrupt_handle, &ctx->interrupt_context); cyg_drv_interrupt_attach (ctx->interrupt_handle); HAL_VSR_SET (LEON_ITC2_HIGH_PRIORITY_ITC1_IT, phy_vsr, NULL); phy_interrupt_data = (cyg_addrword_t) ctx; LEON_ITC2_CLEAR = PHY_ITC2_MASK; LEON_ITC2_LEVEL |= PHY_ITC2_MASK; LEON_ITC2_MASK |= PHY_ITC2_MASK; cyg_drv_interrupt_unmask (LEON_ITC2_HIGH_PRIORITY_ITC1_IT); /* Activate clocks and release DSP reset. */ phy_init_dsp (); /* Initialise hardware. */ phy_clock_set_numerator (ctx, 1000000); PHY_PRATIC_IMMEDIATE_ACTION = PHY_PRATIC_ACTION__INIT_TX; PHY_PRATIC_IMMEDIATE_ACTION = PHY_PRATIC_ACTION__INIT_RX; PHY_PRATIC_IMMEDIATE_ACTION = PHY_PRATIC_ACTION__START_ACQU; PHY_DSPSS_COMMON_MODE = BF_FILL (PHY_DSPSS_COMMON_MODE, (USE_CRC_FC, 1), (USE_CRC_PB, 1)); PHY_DSPSS_TX_SCALE_ADAPT = PHY_PARAM_TX_SCALE_ADAPT; PHY_DSPSS_TX_SCALE_ADAPT_BLK_EXP = PHY_PARAM_TX_SCALE_ADAPT_BLK_EXP; PHY_DSPSS_RX_FEC_PARAM = PHY_PARAMS (PHY_DSPSS, RX_FEC_PARAM, ME10DOI_MAX_IT, TCC_HALF_IT_FC, TCC_DYN_STOP, TCC_BER_PERIOD, TCC_THRESHOLD) | BF_FILL (PHY_DSPSS_RX_FEC_PARAM, (TCC_HALF_IT_DATA, PHY_PARAM_RX_FEC_PARAM__TCC_HALF_IT_DATA_LAST_PB)); PHY_DSPSS_CHANNEL_ESTIM_COEF = PHY_PARAMS (PHY_DSPSS, CHANNEL_ESTIM_COEF, COEF_PREAMBLE) | BF_FILL (PHY_DSPSS_CHANNEL_ESTIM_COEF, (COEF_RES_DELTA, PHY_PARAM_CHANNEL_ESTIM_COEF__COEF_RES_DELTA_INTERNAL)); PHY_DSPSS_RESYS_THRESHOLD = PHY_PARAMS (PHY_DSPSS, RESYS_THRESHOLD, NB_SYNCP_1, NB_SYNCP_2); PHY_DSPSS_OSYSEQ_CONFIG = BF_FILL (PHY_DSPSS_OSYSEQ_CONFIG, (STOP_CONDITION, PHY_DSPSS_OSYSEQ_CONFIG__STOP_CONDITION__RX_PRE)); PHY_DSPSS_DETECT_PARAM_1 = PHY_PARAMS (PHY_DSPSS, DETECT_PARAM_1, LAMBDA, GAMMA); PHY_DSPSS_DETECT_PARAM_2 = PHY_PARAMS (PHY_DSPSS, DETECT_PARAM_2, DMIN, DMAX); PHY_DSPSS_DETECT_PARAM_3 = PHY_PARAMS (PHY_DSPSS, DETECT_PARAM_3, AFE_IN_BLOCK_EXP, USE_BAND_0, USE_BAND_1, MSB_FILTER_0_CHOICE, MSB_FILTER_1_CHOICE); PHY_DSPSS_DETECT_PARAM_4 = PHY_PARAMS (PHY_DSPSS, DETECT_PARAM_4, DELAY_BEFORE_DETECT, DELAY_FREEZE, ACTIVATION_INIT_REFERENCE); PHY_DSPSS_MAGIC_PARAM_1 = PHY_PARAMS (PHY_DSPSS, MAGIC_PARAM_1, TARGET_NRJ, CONVERGENCE_TOLERANCE); PHY_DSPSS_MAGIC_PARAM_2 = PHY_PARAMS (PHY_DSPSS, MAGIC_PARAM_2, DIVERGENCE_TOLERANCE); PHY_DSPSS_MAGIC_PARAM_3 = PHY_PARAMS (PHY_DSPSS, MAGIC_PARAM_3, MAX_GAIN, OVERFLOW_GAIN_RESET, AGC_MANUAL, AGC_MANUAL_START); PHY_DSPSS_USED_CARRIERS = PHY_CARRIER_NB; PHY_DSPSS_FIRST_CARRIER = PHY_CARRIER_OFFSET; PHY_DSPSS_USED_CARRIERS_EXT10 = PHY_HP10_CARRIER_NB; PHY_DSPSS_FIRST_CARRIER_EXT10 = PHY_HP10_CARRIER_OFFSET; /* SPOC. */ PHY_DSPSS_SPOC_DEBUG_MODE = BF_FILL (PHY_DSPSS_SPOC_DEBUG_MODE, (BYPASS, 1)); /* MIA. */ PHY_DSPSS_MIA_PARAM = PHY_PARAMS (PHY_DSPSS, MIA_PARAM, USE_SNR_IN_LLR); PHY_DSPSS_MIA_SF_BPSK = PHY_PARAM_MIA_SF_BPSK; PHY_DSPSS_MIA_SF_QPSK = PHY_PARAM_MIA_SF_QPSK; PHY_DSPSS_MIA_SF_QAM8_re = PHY_PARAM_MIA_SF_QAM8_RE; PHY_DSPSS_MIA_SF_QAM8_im = PHY_PARAM_MIA_SF_QAM8_IM; PHY_DSPSS_MIA_SF_QAM16 = PHY_PARAM_MIA_SF_QAM16; PHY_DSPSS_MIA_SF_QAM64 = PHY_PARAM_MIA_SF_QAM64; PHY_DSPSS_MIA_SF_QAM256 = PHY_PARAM_MIA_SF_QAM256; PHY_DSPSS_MIA_SF_QAM1024 = PHY_PARAM_MIA_SF_QAM1024; /* Send RESYS program and offset table. */ phy_resys_load (ctx); PHY_DSPSS_RESYS_PARAM = BF_FILL (PHY_DSPSS_RESYS_PARAM, (RESYS_COND, 0), (RESYS_ON, 1)); /* Register stats. */ phy_init_stats (ctx); /* Register tunable parameters. */ phy_init_tunable_param (ctx); /* Done. */ return ctx; } static void phy_set_robo_param (phy_t *ctx, u32 *tonemask, uint carrier_nb) { static const uint n_copies_table[PHY_MOD_ROBO_NB] = { 4, 2, 5 }; static const uint n_raw[PHY_MOD_ROBO_NB] = { 520 * 8 * 2, 520 * 8 * 2, 136 * 8 * 2 }; static volatile u32 * const robo_interleaver_1[PHY_MOD_ROBO_NB] = { &PHY_DSPSS_STD_ROBO_INTERLEAVER_1, &PHY_DSPSS_HS_ROBO_INTERLEAVER_1, &PHY_DSPSS_MINI_ROBO_INTERLEAVER_1 }; static volatile u32 * const robo_interleaver_2[PHY_MOD_ROBO_NB] = { &PHY_DSPSS_STD_ROBO_INTERLEAVER_2, &PHY_DSPSS_HS_ROBO_INTERLEAVER_2, &PHY_DSPSS_MINI_ROBO_INTERLEAVER_2 }; phy_mod_t i; for (i = PHY_MOD_ROBO; i < PHY_MOD_ROBO_NB; i++) { uint n_copies = n_copies_table[i]; uint carrier_in_segment = carrier_nb / n_copies; uint carrier_nb_robo = carrier_in_segment * n_copies; uint bits_per_symbol = 2 * carrier_nb_robo; uint bits_in_segment = 2 * carrier_in_segment; uint bits_in_last_symbol = n_raw[i] % bits_per_symbol; uint bits_in_last_segment; if (bits_in_last_symbol == 0) { bits_in_last_symbol = bits_per_symbol; bits_in_last_segment = bits_in_segment; } else { bits_in_last_segment = bits_in_last_symbol - bits_in_segment * ((bits_in_last_symbol - 1) / bits_in_segment); } uint n_pad = bits_in_segment - bits_in_last_segment; /* Cycle shift. */ u32 cycle_shift; switch (i) { case PHY_MOD_ROBO: if (bits_in_last_symbol <= bits_in_segment) cycle_shift = BF_FILL (PHY_DSPSS_X_ROBO_INTERLEAVER_2, (CYCLE_SHIFT_0, 0), (CYCLE_SHIFT_1, 0), (CYCLE_SHIFT_2, 0), (CYCLE_SHIFT_3, 0)); else if (bits_in_last_symbol <= 2 * bits_in_segment) cycle_shift = BF_FILL (PHY_DSPSS_X_ROBO_INTERLEAVER_2, (CYCLE_SHIFT_0, 0), (CYCLE_SHIFT_1, 0), (CYCLE_SHIFT_2, 1), (CYCLE_SHIFT_3, 1)); else if (bits_in_last_symbol <= 3 * bits_in_segment) cycle_shift = BF_FILL (PHY_DSPSS_X_ROBO_INTERLEAVER_2, (CYCLE_SHIFT_0, 0), (CYCLE_SHIFT_1, 0), (CYCLE_SHIFT_2, 0), (CYCLE_SHIFT_3, 0)); else cycle_shift = BF_FILL (PHY_DSPSS_X_ROBO_INTERLEAVER_2, (CYCLE_SHIFT_0, 0), (CYCLE_SHIFT_1, 1), (CYCLE_SHIFT_2, 2), (CYCLE_SHIFT_3, 3)); break; case PHY_MOD_HS_ROBO: if (bits_in_last_symbol <= bits_in_segment) cycle_shift = BF_FILL (PHY_DSPSS_X_ROBO_INTERLEAVER_2, (CYCLE_SHIFT_0, 0), (CYCLE_SHIFT_1, 0)); else cycle_shift = BF_FILL (PHY_DSPSS_X_ROBO_INTERLEAVER_2, (CYCLE_SHIFT_0, 0), (CYCLE_SHIFT_1, 1)); break; case PHY_MOD_MINI_ROBO: if (bits_in_last_symbol <= 4 * bits_in_segment) cycle_shift = BF_FILL (PHY_DSPSS_X_ROBO_INTERLEAVER_2, (CYCLE_SHIFT_0, 0), (CYCLE_SHIFT_1, 0), (CYCLE_SHIFT_2, 0), (CYCLE_SHIFT_3, 0), (CYCLE_SHIFT_4, 0)); else cycle_shift = BF_FILL (PHY_DSPSS_X_ROBO_INTERLEAVER_2, (CYCLE_SHIFT_0, 0), (CYCLE_SHIFT_1, 1), (CYCLE_SHIFT_2, 2), (CYCLE_SHIFT_3, 3), (CYCLE_SHIFT_4, 4)); break; default: dbg_assert_default (); } /* Last carrier index. */ uint j, k, n; u32 tkw; n = 0; for (j = 0; j < PHY_TONEMASK_WORDS; j++) { tkw = tonemask[j]; for (k = 0; k < 32; k++) { if (!(tkw & 1)) n++; if (n == carrier_nb_robo) break; tkw >>= 1; } if (n == carrier_nb_robo) break; } dbg_assert (n == carrier_nb_robo); uint last_carrier_index = j * 32 + k; /* Write registers. */ *robo_interleaver_1[i] = BF_FILL (PHY_DSPSS_X_ROBO_INTERLEAVER_1, (NPAD, n_pad), (BITS_IN_SEGMENT, bits_in_segment)); *robo_interleaver_2[i] = cycle_shift | BF_SHIFT (PHY_DSPSS_X_ROBO_INTERLEAVER_2__MAX_CARRIER, last_carrier_index); } } static void phy_set_tunable_param (phy_t *ctx) { uint i; for (i = 0; i < COUNT (phy_tunable.mafadese_coef_filter_band0); i++) PHY_DSPSS_MAFADESE_COEF_FILTER_BAND_0_n[i] = phy_tunable.mafadese_coef_filter_band0[i]; for (i = 0; i < COUNT (phy_tunable.mafadese_coef_filter_band1); i++) PHY_DSPSS_MAFADESE_COEF_FILTER_BAND_1_n[i] = phy_tunable.mafadese_coef_filter_band1[i]; phy_spoc_init (ctx, phy_tunable.spoc_rho_initial_q30); PHY_DSPSS_CHANNEL_ESTIM_COEF = PHY_PARAMS (PHY_DSPSS, CHANNEL_ESTIM_COEF, COEF_PREAMBLE) | BF_FILL ( PHY_DSPSS_CHANNEL_ESTIM_COEF, (COEF_RES_DELTA, phy_tunable.delta_res_coef_internal)); } static void phy_tonemask_to_ampmap (u32 *tonemask, u32 *ampmap) { uint i, j; u32 tmw = 0, amw; for (i = 0; i < PHY_TONEMAP_WORDS; i++) { if (i % 4 == 0) tmw = *tonemask++; amw = 0; for (j = 0; j < 8; j++) { amw >>= 4; if (tmw & 1) amw |= 0xf0000000; tmw >>= 1; } *ampmap++ = amw; } } void phy_set_tonemask (phy_t *ctx, u32 *tonemask, uint carrier_nb) { /* Do not use stack for hardware descriptor, it may be local ram. */ dbg_assert (ctx); dbg_assert_ptr (tonemask); dbg_assert (ARCH_DMA_VALID (tonemask)); dbg_assert (((u32) tonemask & 0x3) == 0); PHY_TRACE (SET_TONEMASK, carrier_nb); /* Send new tonemask. */ static phy_tmdma_desc_t desc; desc.next = NULL; desc.data = tonemask; desc.size_words = PHY_TONEMASK_WORDS; desc.mem_index = PHY_TMDMA_MEM_TONEMASK; desc.last = 1; desc.local_start_addr = 0; arch_write_buffer_flush (); PHY_DSPSS_TMD_DESC_ADDR = (u32) ARCH_CPU_TO_DMA (&desc); dbg_assert (!(PHY_DSPSS_TMD_CTRL & BF_MASK (PHY_DSPSS_TMD_CTRL__BUSY))); PHY_PRATIC_IMMEDIATE_ACTION = PHY_PRATIC_ACTION__TMD_START; /* XXX: Wait completion until TMDMA bug is fixed. */ while (PHY_DSPSS_TMD_CTRL & BF_MASK (PHY_DSPSS_TMD_CTRL__BUSY)) ; /* Send amplitude map. */ static phy_tmdma_desc_t am0, am1; static u32 ampmap[PHY_TONEMAP_WORDS]; phy_tonemask_to_ampmap (tonemask, ampmap); am0.next = ARCH_CPU_TO_DMA (&am1); am0.data = ARCH_CPU_TO_DMA (ampmap); am0.size_words = BLK_SIZE / 4; am0.mem_index = PHY_TMDMA_MEM_AMPLITUDE_MAP; am0.last = 0; am0.local_start_addr = 0; am1.next = NULL; am1.data = ARCH_CPU_TO_DMA (ampmap + BLK_SIZE / 4); am1.size_words = PHY_TONEMAP_WORDS - BLK_SIZE / 4; am1.mem_index = PHY_TMDMA_MEM_AMPLITUDE_MAP; am1.last = 1; am1.local_start_addr = BLK_SIZE; arch_write_buffer_flush (); PHY_DSPSS_TMD_DESC_ADDR = (u32) ARCH_CPU_TO_DMA (&am0); dbg_assert (!(PHY_DSPSS_TMD_CTRL & BF_MASK (PHY_DSPSS_TMD_CTRL__BUSY))); PHY_PRATIC_IMMEDIATE_ACTION = PHY_PRATIC_ACTION__TMD_START; /* XXX: Wait completion until TMDMA bug is fixed. */ while (PHY_DSPSS_TMD_CTRL & BF_MASK (PHY_DSPSS_TMD_CTRL__BUSY)) ; /* Set AV parameters. */ #define PHY_CARRIER_NB_10_FC 76 PHY_DSPSS_HPAV_MASK = BF_FILL (PHY_DSPSS_HPAV_MASK, (NB_CARRIER, carrier_nb), (NB_CARRIER_10, PHY_CARRIER_NB_10_FC)); /* Set HP 1.0 parameters. */ PHY_DSPSS_HP10_MASK_0 = 0x0C060F01; PHY_DSPSS_HP10_MASK_1 = 0xC0000400; PHY_DSPSS_HP10_MASK_2 = 0x000C0001; PHY_DSPSS_HP10_MASK_3 = 0x0060001E; PHY_DSPSS_HP10_MASK_4 = 0x0000FFE0; PHY_DSPSS_HP10_MASK_5 = 0x00000000; PHY_DSPSS_HP10_FC_MASK_0 = 0x20006000; PHY_DSPSS_HP10_FC_MASK_1 = 0x000E0000; PHY_DSPSS_HP10_FC_MASK_2 = 0xFFF00060; /* Set tunable parameters. */ phy_set_tunable_param (ctx); /* Set ROBO parameters. */ phy_set_robo_param (ctx, tonemask, carrier_nb); /* Create preamble and PRS. */ PHY_DSPSS_TX_PARAM = PHY_DSPSS_TX_PARAM__DEFAULT; phy_prepare (ctx, PHY_PREPARE_TYPE_PREAMBLE, true); phy_prepare (ctx, PHY_PREPARE_TYPE_PRS, true); } void phy_uninit (phy_t *ctx) { dbg_assert (ctx); PHY_TRACE (UNINIT); /* Stop hardware. */ PHY_PRATIC_TIMER_1_CTRL = 0; PHY_PRATIC_TIMER_2_CTRL = 0; PHY_PRATIC_TIMER_3_CTRL = 0; PHY_PRATIC_TIMER_4_CTRL = 0; PHY_PRATIC_TIMER_5_CTRL = 0; PHY_PRATIC_TIMER_6_CTRL = 0; PHY_PRATIC_TIMER_7_CTRL = 0; PHY_PRATIC_IMMEDIATE_ACTION = PHY_PRATIC_ACTION__INIT_TX; PHY_PRATIC_IMMEDIATE_ACTION = PHY_PRATIC_ACTION__INIT_RX; /* Detach interrupt. */ cyg_drv_interrupt_mask (LEON_ITC2_HIGH_PRIORITY_ITC1_IT); cyg_drv_interrupt_detach (ctx->interrupt_handle); cyg_drv_interrupt_delete (ctx->interrupt_handle); } void phy_reset (phy_t *ctx) { dbg_assert (ctx); PHY_TRACE (RESET); /* TODO */ } void phy_prepare (phy_t *ctx, phy_prepare_type_t type, bool wait) { dbg_assert (ctx); dbg_assert (type < PHY_PREPARE_TYPE_NB); PHY_TRACE (PREPARE, phy_date (), type, wait); static uint action[PHY_PREPARE_TYPE_NB] = { PHY_PRATIC_ACTION__CREATE_PRE, PHY_PRATIC_ACTION__CREATE_PRS, }; /* Launch action. */ PHY_PRATIC_IMMEDIATE_ACTION = action[type]; while (wait && (PHY_DSPSS_TIME_CHAIN_INFO & BF_MASK (PHY_DSPSS_TIME_CHAIN_INFO__CREATION_IN_PROGRESS))) ; } u32 phy_seed (void) { u32 seed; uint i; /* Activate clocks and release DSP reset. */ phy_init_dsp (); /* Start acquisition. */ PHY_PRATIC_IMMEDIATE_ACTION = PHY_PRATIC_ACTION__START_ACQU; /* Enable AGC. */ PHY_DSPSS_MAGIC_PARAM_1 = PHY_PARAMS (PHY_DSPSS, MAGIC_PARAM_1, TARGET_NRJ, CONVERGENCE_TOLERANCE); PHY_DSPSS_MAGIC_PARAM_2 = PHY_PARAMS (PHY_DSPSS, MAGIC_PARAM_2, DIVERGENCE_TOLERANCE); PHY_DSPSS_MAGIC_PARAM_3 = PHY_PARAMS (PHY_DSPSS, MAGIC_PARAM_3, MAX_GAIN, OVERFLOW_GAIN_RESET) | BF_FILL (PHY_DSPSS_MAGIC_PARAM_3, (AGC_MANUAL, 1), (AGC_MANUAL_START, 1)); /* Make acquisition, wait until the whole RX MEM has been filled twice. */ u32 stop = phy_sysdate () + 16 * 384 * 2 / 3; while (less_mod2p32 (phy_sysdate (), stop)) ; /* Stop AGC and acquisition. */ PHY_DSPSS_MAGIC_PARAM_3 = PHY_PARAMS (PHY_DSPSS, MAGIC_PARAM_3, MAX_GAIN, OVERFLOW_GAIN_RESET); PHY_PRATIC_IMMEDIATE_ACTION = PHY_PRATIC_ACTION__STOP_ACQU; /* Read from RX MEM. */ volatile u32 *rx_mem = &PHY_BASE_ADDR_MAB_RX_MEM; PHY_DSPSS_MABEILLE_MODE = BF_MASK (PHY_DSPSS_MABEILLE_MODE__DEBUG_MODE_RX_MEM); seed = 0; for (i = 0; i < 32; i++) seed = ((seed << 1) | (seed >> 31)) ^ rx_mem[i * (256 + 1)]; PHY_DSPSS_MABEILLE_MODE = 0; /* Return seed. */ return seed; } void phy_deferred_schedule (phy_t *ctx) { /* We need this undocumented eCos function to repost DSR. */ extern void cyg_interrupt_post_dsr (CYG_ADDRWORD intr_obj); dbg_assert (ctx); cyg_interrupt_post_dsr (ctx->interrupt_handle); } void ARCH_ILRAM phy_set_tonemap (phy_t *ctx, blk_t *tonemap) { dbg_claim (ctx); dbg_claim_ptr (tonemap); dbg_claim (ARCH_DMA_VALID (tonemap)); dbg_claim_ptr (tonemap->data); dbg_claim (ARCH_DMA_VALID (tonemap->data)); dbg_claim_ptr (tonemap->next); dbg_claim (ARCH_DMA_VALID (tonemap->next)); dbg_claim_ptr (tonemap->next->data); dbg_claim (ARCH_DMA_VALID (tonemap->next->data)); PHY_TRACE (SET_TONEMAP, tonemap); dbg_do (phy_debug.last_tonemap = tonemap); /* Send new tonemap. */ dbg_claim (PHY_TONEMAP_WORDS > BLK_SIZE / 4 && PHY_TONEMAP_WORDS < 2 * BLK_SIZE / 4); phy_tmdma_desc_t *desc = (phy_tmdma_desc_t *) tonemap; desc->size_words = BLK_SIZE / 4; desc->mem_index = PHY_TMDMA_MEM_TONEMAP_0; desc->last = 0; desc->local_start_addr = 0; desc->next->size_words = PHY_TONEMAP_WORDS - BLK_SIZE / 4; desc->next->mem_index = PHY_TMDMA_MEM_TONEMAP_0; desc->next->last = 1; desc->next->local_start_addr = BLK_SIZE / 4; arch_write_buffer_flush (); PHY_DSPSS_TMD_DESC_ADDR = (u32) desc; dbg_assert (!(PHY_DSPSS_TMD_CTRL & BF_MASK (PHY_DSPSS_TMD_CTRL__BUSY))); PHY_PRATIC_IMMEDIATE_ACTION = PHY_PRATIC_ACTION__TMD_START; /* XXX: Wait completion until TMDMA bug is fixed. */ uint timeout = 32768; /* This is more than enough. */ while (PHY_DSPSS_TMD_CTRL & BF_MASK (PHY_DSPSS_TMD_CTRL__BUSY) && timeout) timeout--; dbg_assert (timeout); } void phy_freq_error_set (phy_t *ctx, bool sync, s32 rho_q30) { dbg_claim (ctx); if (sync != ctx->freq_error_sync) { uint delta_res_coef = sync ? phy_tunable.delta_res_coef_external : phy_tunable.delta_res_coef_internal; PHY_DSPSS_CHANNEL_ESTIM_COEF = PHY_PARAMS (PHY_DSPSS, CHANNEL_ESTIM_COEF, COEF_PREAMBLE) | BF_FILL (PHY_DSPSS_CHANNEL_ESTIM_COEF, (COEF_RES_DELTA, delta_res_coef)); phy_resys_patch (ctx, sync); ctx->freq_error_sync = sync; } PHY_DSPSS_MACACKE_DELTA_USED = fixed_round ( rho_q30, 30 - PHY_DSPSS_MACACKE_DELTA_USED__VALUE__RHO_Q); }