/******************************************************************************* | |
* (c) Copyright 2010 Actel Corporation. All rights reserved. | |
* | |
* This file contains the implementation of the functions used to dynamically | |
* control the linear transforms applied by the ACE post processing engine to | |
* the samples read from the SSE. | |
* | |
* SVN $Revision: 2908 $ | |
* SVN $Date: 2010-08-20 16:01:28 +0100 (Fri, 20 Aug 2010) $ | |
*/ | |
#include "mss_ace.h" | |
#include "mss_ace_configurator.h" | |
#include "mtd_data.h" | |
#include "envm_layout.h" | |
#include "../../CMSIS/a2fxxxm3.h" | |
#include "../../CMSIS/mss_assert.h" | |
#include "../../drivers_config/mss_ace/ace_config.h" | |
#ifdef __cplusplus | |
extern "C" { | |
#endif | |
/* | |
* The ACE_set_linear_transform() is only available when using ACE configuration | |
* files generated by Libero 9.1 or later. | |
*/ | |
#ifdef ACE_CFG_DATA_FORMAT_VERSION | |
/*------------------------------------------------------------------------------ | |
* Masks ans shift values used to derive the ABPS ranges from the analog block | |
* configuration. | |
*/ | |
#define ABPS1_CFG_BITS_MASK (uint32_t)0x06 | |
#define ABPS1_CFG_BITS_SHIFT (uint32_t)1 | |
#define ABPS2_CFG_BITS_MASK (uint32_t)0x60 | |
#define ABPS2_CFG_BITS_SHIFT (uint32_t)5 | |
/*------------------------------------------------------------------------------ | |
* One Bit DAC definitions. | |
*/ | |
#define OBD_CURRENT (uint32_t)1 | |
#define OBD_VOLTAGE (uint32_t)0 | |
#define OBD_MODE_MASK (uint32_t)0x01 | |
#define OBD_CHOPPING_MASK (uint32_t)0x02 | |
/*-------------------------------------------------------------------------*//** | |
Neutral factor and offset for m*x + c trnasform. | |
*/ | |
#define NEUTRAL_M_FACTOR 0x4000 | |
#define NEUTRAL_C_OFFSET 0x0000 | |
/*-------------------------------------------------------------------------*//** | |
Enumearation of the various input channel types. This is used to differentiate | |
between channel types in order to extract the relevant factory calibration | |
data(m1 and c1). | |
*/ | |
typedef enum channel_type | |
{ | |
ABPS1_CHAN = 0, | |
ABPS2_CHAN, | |
CMB_CHAN, | |
TMB_CHAN, | |
DIRECT_ADC_INPUT_CHAN, | |
OBDOUT_CHAN, | |
FLOATING_CHAN | |
} cal_channel_type_t; | |
/*-------------------------------------------------------------------------*//** | |
This data structure is used to store factory calibration data for a specific | |
analog input. | |
*/ | |
typedef struct __channel_calibration_t | |
{ | |
uint16_t mext; | |
uint16_t m1; | |
uint16_t c1; | |
} channel_calibration_t; | |
/*-------------------------------------------------------------------------*//** | |
Local functions | |
*/ | |
int32_t extend_sign | |
( | |
uint16_t x | |
); | |
uint32_t adjust_to_24bit_ace_format | |
( | |
int64_t signed48 | |
); | |
uint32_t adjust_to_16bit_ace_format | |
( | |
int64_t signed48 | |
); | |
void get_calibration | |
( | |
adc_channel_id_t channel_id, | |
channel_calibration_t * p_calibration | |
); | |
void write_transform_coefficients | |
( | |
ace_channel_handle_t channel_handle, | |
uint32_t m, | |
uint32_t c | |
); | |
/*-------------------------------------------------------------------------*//** | |
*/ | |
extern const uint8_t g_ace_external_varef_used[ACE_NB_OF_ADC]; | |
extern ace_channel_desc_t g_ace_channel_desc_table[ACE_NB_OF_INPUT_CHANNELS]; | |
extern const ppe_transforms_desc_t g_ace_ppe_transforms_desc_table[ACE_NB_OF_INPUT_CHANNELS]; | |
/*------------------------------------------------------------------------------ | |
* Pointer to the manufacturing test data containing trimming information | |
* generated during manufacturing. | |
*/ | |
static const mtd_data_t * const p_mtd_data = (mtd_data_t *)MTD_ADDRESS; | |
/*-------------------------------------------------------------------------*//** | |
See "mss_ace.h" for details of how to use this function. | |
*/ | |
int16_t ACE_get_default_m_factor | |
( | |
ace_channel_handle_t channel_handle | |
) | |
{ | |
ASSERT( channel_handle < NB_OF_ACE_CHANNEL_HANDLES ); | |
return g_ace_ppe_transforms_desc_table[channel_handle].m_ppe_offset; | |
} | |
/*-------------------------------------------------------------------------*//** | |
See "mss_ace.h" for details of how to use this function. | |
*/ | |
int16_t ACE_get_default_c_offset | |
( | |
ace_channel_handle_t channel_handle | |
) | |
{ | |
ASSERT( channel_handle < NB_OF_ACE_CHANNEL_HANDLES ); | |
return g_ace_ppe_transforms_desc_table[channel_handle].c_ppe_offset; | |
} | |
/*-------------------------------------------------------------------------*//** | |
See "mss_ace.h" for details of how to use this function. | |
m = m2 * m1 * mext | |
c = (m2 * c1 * mext) + (c2 * mext) | |
*/ | |
void ACE_set_linear_transform | |
( | |
ace_channel_handle_t channel_handle, | |
int16_t m2, | |
int16_t c2 | |
) | |
{ | |
adc_channel_id_t channel_id; | |
uint32_t m; | |
uint32_t c; | |
int32_t m32; | |
int64_t m64; | |
int32_t c32; | |
int64_t c64_1; | |
int64_t c64_2; | |
uint16_t m1; | |
uint16_t c1; | |
uint16_t mext; | |
channel_calibration_t calibration; | |
ASSERT( channel_handle < NB_OF_ACE_CHANNEL_HANDLES ); | |
if(channel_handle < NB_OF_ACE_CHANNEL_HANDLES) | |
{ | |
channel_id = g_ace_channel_desc_table[channel_handle].signal_id; | |
get_calibration(channel_id, &calibration); | |
m1 = calibration.m1; | |
c1 = calibration.c1; | |
mext = calibration.mext; | |
/* | |
* m = m2 * m1 * mext | |
*/ | |
m32 = extend_sign(m2) * extend_sign(m1); | |
m64 = (int64_t)m32 * extend_sign(mext); | |
/* Convert 48-bit result to 32-bit ACE format result. */ | |
m = adjust_to_16bit_ace_format(m64); | |
/* | |
* c = (m2 * c1 * mext) + (c2 * mext) | |
*/ | |
c32 = extend_sign(m2) * extend_sign(c1); | |
c64_1 = (int64_t)c32 * extend_sign(mext); | |
c64_2 = ((int64_t)(extend_sign(c2) * extend_sign(mext))) << 14; | |
c = adjust_to_24bit_ace_format(c64_1 + c64_2); | |
write_transform_coefficients(channel_handle, m, c); | |
} | |
} | |
/*-------------------------------------------------------------------------*//** | |
Extend 16-bit signed number to 32-bit signed number. | |
*/ | |
int32_t extend_sign | |
( | |
uint16_t x | |
) | |
{ | |
int32_t y; | |
const uint32_t sign_bit_mask = 0x00008000u; | |
y = (x ^ sign_bit_mask) - sign_bit_mask; | |
return y; | |
} | |
/*-------------------------------------------------------------------------*//** | |
Take a 48-bit signed number, adjust it for saturation in the range -8 to | |
+7.999, translate into 24-bit ACE format. | |
*/ | |
uint32_t adjust_to_24bit_ace_format | |
( | |
int64_t signed48 | |
) | |
{ | |
int32_t ace24_format; | |
const int64_t MAX_POSITIVE = 0x00001FFFFFFFFFFFuLL; /* +7.9999 */ | |
const int64_t MIN_NEGATIVE = 0xFFFF200000000000uLL; /* -8 */ | |
/* Check saturation. */ | |
if(signed48 > MAX_POSITIVE) | |
{ | |
signed48 = MAX_POSITIVE; | |
} | |
else if(signed48 < MIN_NEGATIVE) | |
{ | |
signed48 = MIN_NEGATIVE; | |
} | |
/* Adjust to 24-bit ACE format. */ | |
ace24_format = (uint32_t)(signed48 >> 14); | |
return ace24_format; | |
} | |
/*-------------------------------------------------------------------------*//** | |
Take a 48-bit signed number, adjust it for saturation in the range -8 to | |
+7.999, translate into 16-bit ACE format. | |
*/ | |
uint32_t adjust_to_16bit_ace_format | |
( | |
int64_t signed48 | |
) | |
{ | |
int32_t ace24_format; | |
const int64_t MAX_POSITIVE = 0x00001FFFFFFFFFFFuLL; /* +7.9999 */ | |
const int64_t MIN_NEGATIVE = 0xFFFF200000000000uLL; /* -8 */ | |
/* Check saturation. */ | |
if(signed48 > MAX_POSITIVE) | |
{ | |
signed48 = MAX_POSITIVE; | |
} | |
else if(signed48 < MIN_NEGATIVE) | |
{ | |
signed48 = MIN_NEGATIVE; | |
} | |
/* Adjust to 24-bit ACE format. */ | |
ace24_format = (uint32_t)(signed48 >> 20); | |
return ace24_format; | |
} | |
/*-------------------------------------------------------------------------*//** | |
*/ | |
void get_calibration | |
( | |
adc_channel_id_t channel_id, | |
channel_calibration_t * p_calibration | |
) | |
{ | |
const uint32_t channel_mask = 0x0000000F; | |
const uint32_t CMB_MUX_SEL_MASK = 0x01; | |
const uint32_t TMB_MUX_SEL_MASK = 0x01; | |
const cal_channel_type_t channel_type_lut[16] = | |
{ | |
FLOATING_CHAN, | |
ABPS1_CHAN, | |
ABPS2_CHAN, | |
CMB_CHAN, | |
TMB_CHAN, | |
ABPS1_CHAN, | |
ABPS2_CHAN, | |
CMB_CHAN, | |
TMB_CHAN, | |
DIRECT_ADC_INPUT_CHAN, | |
DIRECT_ADC_INPUT_CHAN, | |
DIRECT_ADC_INPUT_CHAN, | |
DIRECT_ADC_INPUT_CHAN, | |
FLOATING_CHAN, | |
FLOATING_CHAN, | |
OBDOUT_CHAN | |
}; | |
cal_channel_type_t channel_type; | |
uint32_t channel_nb; | |
uint32_t adc_nb; | |
uint32_t range; | |
uint32_t quad_id; | |
mtd_calibration_mc_t const * p_mc_coeff = 0; | |
channel_nb = channel_id & channel_mask; | |
channel_type = channel_type_lut[channel_nb]; | |
adc_nb = ((uint32_t)channel_id & 0x30u) >> 4u; | |
quad_id = adc_nb * 2; | |
if ( (channel_nb > 4) && (channel_nb < 9) ) { ++quad_id; } | |
switch ( channel_type ) | |
{ | |
case ABPS1_CHAN: | |
range = (ACE->ACB_DATA[quad_id].b8 & ABPS1_CFG_BITS_MASK) >> ABPS1_CFG_BITS_SHIFT; | |
p_mc_coeff = &p_mtd_data->abps_calibration[quad_id][0][range]; | |
break; | |
case ABPS2_CHAN: | |
range = (ACE->ACB_DATA[quad_id].b8 & ABPS2_CFG_BITS_MASK) >> ABPS2_CFG_BITS_SHIFT; | |
p_mc_coeff = &p_mtd_data->abps_calibration[quad_id][1][range]; | |
break; | |
case CMB_CHAN: | |
{ | |
uint32_t cmb_mux_sel = (uint32_t)ACE->ACB_DATA[quad_id].b9 & CMB_MUX_SEL_MASK; | |
if ( cmb_mux_sel == 0 ) | |
{ /* current monitor */ | |
p_mc_coeff = &p_mtd_data->cm_calibration[quad_id]; | |
} | |
else | |
{ /* direct input */ | |
p_mc_coeff = &p_mtd_data->quads_direct_input_cal[quad_id][0]; | |
} | |
} | |
break; | |
case TMB_CHAN: | |
{ | |
uint32_t tmb_mux_sel = (uint32_t)ACE->ACB_DATA[quad_id].b10 & TMB_MUX_SEL_MASK; | |
if ( tmb_mux_sel == 0 ) | |
{ /* temperature monitor */ | |
p_mc_coeff = &p_mtd_data->tm_calibration[quad_id]; | |
} | |
else | |
{ /* direct input */ | |
p_mc_coeff = &p_mtd_data->quads_direct_input_cal[quad_id][1]; | |
} | |
} | |
break; | |
case DIRECT_ADC_INPUT_CHAN: | |
{ | |
const uint32_t channel_to_direct_in_lut[16] | |
= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 0, 0, 0}; | |
uint32_t direct_in_id; | |
direct_in_id = channel_to_direct_in_lut[channel_id & channel_mask]; | |
p_mc_coeff = &p_mtd_data->adc_direct_input_cal[adc_nb][direct_in_id]; | |
} | |
break; | |
case OBDOUT_CHAN: | |
{ | |
uint32_t obd_mode = (uint32_t)ACE->ACB_DATA[quad_id].b6 & OBD_MODE_MASK; | |
uint32_t chopping_option = (uint32_t)ACE->ACB_DATA[quad_id].b6 & OBD_CHOPPING_MASK; | |
if (obd_mode > 0) | |
{ | |
obd_mode = 1; | |
} | |
if (chopping_option > 0) | |
{ | |
chopping_option = 1; | |
} | |
p_mc_coeff = &p_mtd_data->obd_calibration[adc_nb][obd_mode][chopping_option]; | |
} | |
break; | |
case FLOATING_CHAN: | |
default: | |
/* Give neutral values is invalid channel. */ | |
p_calibration->m1 = NEUTRAL_M_FACTOR; | |
p_calibration->c1 = NEUTRAL_C_OFFSET; | |
break; | |
} | |
if (p_mc_coeff != 0) | |
{ | |
p_calibration->m1 = p_mc_coeff->m; | |
p_calibration->c1 = p_mc_coeff->c; | |
} | |
/*-------------------------------------------------------------------------- | |
Retrieve the value of the mext factor. This depends if external VAREF is | |
used by the ADC sampling the analog input channel. | |
*/ | |
if (g_ace_external_varef_used[adc_nb]) | |
{ | |
p_calibration->mext = p_mtd_data->global_settings.varef_m; | |
} | |
else | |
{ | |
p_calibration->mext = NEUTRAL_M_FACTOR; | |
} | |
} | |
/*-------------------------------------------------------------------------*//** | |
Write new m and c transform factors into the PPE RAM. The m and c factors | |
should be in 32-bit ACE number format. The factors will be merged with | |
relevant PE opcode into PPE RAM. The 32-bit factors are shifted right by one | |
byte giving a 24-bit ACE number which is then merged with an 8-bit PPE opcode | |
located in the most significant byte of the PPE RAM location. | |
*/ | |
void write_transform_coefficients | |
( | |
ace_channel_handle_t channel_handle, | |
uint32_t m, | |
uint32_t c | |
) | |
{ | |
uint16_t m_ppe_offset; | |
uint16_t c_ppe_offset; | |
const uint32_t PPE_OPCODE_MASK = 0xFF000000u; | |
m_ppe_offset = g_ace_ppe_transforms_desc_table[channel_handle].m_ppe_offset; | |
c_ppe_offset = g_ace_ppe_transforms_desc_table[channel_handle].c_ppe_offset; | |
ACE->PPE_RAM_DATA[m_ppe_offset] | |
= (ACE->PPE_RAM_DATA[m_ppe_offset] & PPE_OPCODE_MASK) | (m >> 8u); | |
ACE->PPE_RAM_DATA[c_ppe_offset] | |
= (ACE->PPE_RAM_DATA[c_ppe_offset] & PPE_OPCODE_MASK) | (c >> 8u); | |
} | |
#endif /* ACE_CFG_DATA_FORMAT_VERSION */ | |
#ifdef __cplusplus | |
} | |
#endif |