## FSC For Dahuatech ##
### 參數 ###
#### config.xml ####
* AlgoPeriodMS: 1000
* PowerRecoveryTimeMS="15000"
* DefaultProfile: 0
* PowerOffPWM: 0
#### SDR ####
* FSC 相關的 SDR 位於 FCT.sdr
* 屬性
* Record Type
* FSC 相關的 SDR 其 Record Type 為 C0
* FSC Record type
* 將 FSC 相關的 SDR 分為以下:
* Fan Map
* Main Domain
* Step Header
* Step Curve
* Clamp Header
* 其中,Main Domain 根據 Sub-Record Type 的不同,又可細分為:
* Domain Configuration
* Step Sub-Record
* Clamp Sub-Record
* Fan Fail Sub-Record
* Fan Off Sub-Record
* Domain Mask + Fan Profile Support
* 描述哪些 Domain 中的哪些 Profile 可以參照該 SDR
### Domain 0 ###
* 預設使用 Profile 0
* Profile 0 所參考的 SDR:
* Step Header ID: 0x20
* Step Curve ID: 0x5B, 0x64
* Clamp Header ID: 0xB0, 0xB1, 0xB2, 0xB3, 0x20, 0x22, 0x23, 0x24, 0x25, 0x26, 0x29, 0x2A, 0x2B, 0x2D, 0x2F, 0x83, 0x84
* SDR 內容:
* Step Header ID - 0x20

* Step Curve ID - 0x5B

* Step Curve ID - 0x64 (Max. Step Control Rules)

* Clamp Header ID - 0x20

* Profile 0 的其他參數:
* Normal Control Value: 20 %
* Fan Fault Boost Value: 100%
* Temperature Threshold Boost Value: 100%
* Chassis Cover Boost Valu: 100%
### PWM 與溫度的關係 ###
* 已知 Domain 0 使用 Profile 0,根據 SDR 內容,以 Sensor# 20 作為該 Domain 參考的 Temp Sensor。將定期讀取其溫度,再透過 INTC_FSC_domain_task_algo_temperature 運算後輸出 PWM。
* INTC_FSC_domain_task_algo_temperature 分別從 Profile0 的 units_step 和 units_clamp 選出其中最大 pwm_contrib。
* units_step 和 units_clamp 紀錄了 Profile0 包含了哪些 Step Header ID/Curve ID 和 Clamp Header ID,值得注意的是,只有 **合法** 的 unit 才會有 pwm_contrib。
* 考慮一種情況,若 Sensor# 20 上一次測量溫度為 90 °C,此次為 95 °C。假設,所選出最大 pwm_contrib 的 unit 其 Header ID/Curve ID 和 Clamp ID 分別是 0x20/0x5B 和 0x20。 那麼此時 PWM 會是多少呢?
* 可分為以下兩個部份來探討:
* 首先,分別算出 unit 的 pwm_contrib
* 接著,將不同 unit 的 pwm_contrib 結合起來,將其稱為 pwm_composite,並考慮 pwm_limit 和 pwm_value_normal 的影響
#### pwm_contrib ####
* 忽略 pwm_sleep 的影響
##### Step #####
```clike=
INTC_FSC_domain_task_algo_temperature_step() {
...
// Handle temperature sensor status rules.
if (unit->status == NORMAL) {
INTC_FSC_domain_task_algo_temperature_rule_step(unit); // compute the contribution from temperature
...
}
...
if (0 != (step_curve->sdr.point_cnt & DOMAIN_MAX_BIT)) {
unit->pwm_limit = 100 - (UINT8)pwm;
} else {
unit->pwm_contrib = (UINT8)pwm;
}
}
```
* 結果:
* 當 Sensor# 20 95°C,Curve ID 為 0x5B 的 unit 其 pwm_contrib 為 **78%**,pwm_limit 則為 0%。
* 值得注意的是,當 Sensor# 20 95°C,Curve ID 為 0x64 的 unit 其 pwm_contrib 為 0%, pwm_limit 也為 0%。
##### Clamp #####
```clike=
INTC_FSC_domain_task_algo_temperature_rule_clamp() {
...
if (temperature > (INT32)(UINT32)pclamp->isdr.sleep_hyst.hysteresis_pos) {
temperature -= (INT32)(UINT32)pclamp->isdr.sleep_hyst.hysteresis_pos;
} else if (temperature < -(INT32)(UINT32)pclamp->isdr.sleep_hyst.hysteresis_neg) {
temperature += (INT32)(UINT32)pclamp->isdr.sleep_hyst.hysteresis_neg;
} else {
temperature = 0;
}
temperature_average = (unit->temperature_last + temperature) * 5;
if ((pclamp->isdr.version == 3) && (temperature > 0) && (temperature > unit->temperature_last)) {
ramp_coeff = pclamp->isdr.ramp_coeff_If;
} else {
ramp_coeff = pclamp->isdr.ramp_coeff_Is;
}
if (pclamp->isdr.version == 3) {
// Add the derivative PWM contribution
// Ramp has +3 decimal places, temp +0, time + 6. 3+0+6-(6-3) = 6
ramp_coeff = pclamp->isdr.ramp_coeff_D;
if ((time_elapsed / 1000) > 0) {
pwm_delta += (INT32)((INT64)ramp_coeff * ((INT64)temperature - (INT64)unit->temperature_last) * 1000LL); // PWM change computed in 1/1000000 of a %
}
}
...
}
```
* 計算步驟:
* diff_temp = sensor_temp - clamp_temp
* temperature_average = ( temperature_last + diff_temp )*5
* pwm_delta = Fast Integral Coefficient * temperature_average/10000 (/10000 可看成 /10 * 1000)
* pwm_delta = pwm_delta + (Derivative Coefficient *(diff_temp - temperature_last))
* 結果:
* 當 Sensor# 20 95°C,而前一次溫度為 90 °C 的情況下,Clamp ID 為 0x20 的 unit 其pwm_contrib 為 **12%** 。
#### pwm_composite ####
```clike=
INTC_FSC_domain_task_algo_temperature() {
...
UINT8 pwm_step = profile->domain_info.pwm_value_normal; // 20%
UINT8 pwm_clamp = 0;
UINT8 pwm_limit = 100;
for (idx = 0; idx < profile->units_step.cnt; idx++) {
INTC_FSC_domain_unit_step_t *unit = &profile->units_step.units[idx];
if (pwm_step < unit->pwm_contrib) {
pwm_step = unit->pwm_contrib;
step_sensor = g_step_headers[unit->step_header_id].sdr.thermometer_nr;
}
pwm_limit = MIN(pwm_limit, 100 - unit->pwm_limit);
}
for (idx = 0; idx < profile->units_clamp.cnt; idx++) {
INTC_FSC_domain_unit_clamp_t *unit = &profile->units_clamp.units[idx];
if (pwm_clamp < unit->pwm_contrib) {
pwm_clamp = unit->pwm_contrib;
clamp_sensor = g_clamp_headers[unit->clamp_header_id].isdr.thermometer_nr;
}
}
...
pwm_composite = pwm_step;
pwm_composite += pwm_clamp;
if (domain->fault_detected == 0) {
// Only enforce domain max if there is no fault condition active
pwm_composite = MIN(pwm_composite, (INT64)(UINT64)pwm_limit);
}
pwm_composite = MAX(pwm_composite, profile->domain_info.pwm_value_normal); // enforce "no less than normal" rule after sleep is applied
...
}
```
* 計算:
* pwm_composite = MIN(pwm_step + pwm_clamp, pwm_limit)
* pwm_composite = MAX(pwm_composite, pwm_value_normal)
* 推論:
* 若 pwm_limit < pwm_step + pwm_clamp,則 pwm_composite 等於 pwm_limit ;反之則為 pwm_step + pwm_clamp。總之,誰小則為誰。
* 但不能比 pwm_value_normal 規定的要來得小。
* 結果:
* 當 Sensor# 20 95°C,首先,pwm_composite 等於 90 % (78 % + 12 %)
* 而 pwm_composite 小於 pwm_limit 再加上大於 pwm_value_normal。
* 所以,**最終 pwm_composite 為 90 %**。
## 參考資料 ##
[Supervyse 3.0 FSC Settings HowTo](http://wiki.insyde.com/index.php/Supervyse_3.0_FSC_Settings_HowTo)
[Supervyse 3.0 FSC Porting Guide](http://wiki.insyde.com/index.php/(Deprecated)_Supervyse_3.0_FSC_Porting_Guide)
[Clamp Calculation](http://wiki.insyde.com/index.php/Supervyse_3.0_Dahuatech_Porting_Detail#Clamp_setting)
*Mail: BMC 热策略细节沟通*