add DFS for FS control

This commit is contained in:
Steven Dan
2026-06-18 17:04:32 +08:00
parent a99f0b2288
commit 585a9f7924
12 changed files with 421 additions and 31 deletions

View File

@@ -1 +1 @@
xflash bin/factory/phaten_module_factory.xe --loader loader.o --upgrade 2 bin/fps_uac1/phaten_module_fps_uac1.xe --upgrade 6 ../app_usb_aud_fosi_c1_lp/bin/bypass_uac1/phaten_module_bypass_uac1.xe -o %1
xflash bin/factory/phaten_module_factory.xe --loader loader.o --upgrade 2 bin/fps_uac1/phaten_module_fps_uac1.xe -o %1

View File

@@ -1 +1 @@
xflash --factory-version 15.3 --target-file src/core/xu316_qf60.xn --upgrade 2 bin/fps_uac1/phaten_module_fps_uac1.xe --upgrade 6 ../app_usb_aud_fosi_c1_lp/bin/bypass_uac1/phaten_module_bypass_uac1.xe -o %1
xflash --factory-version 15.3 --target-file src/core/xu316_qf60.xn --upgrade 2 bin/fps_uac1/phaten_module_fps_uac1.xe -o %1

View File

@@ -20,6 +20,7 @@
#include "roleswitchflag.h"
#include "xua_hid_report.h"
#include "nau88c21.h"
#include "tile1_clk.h"
#if MQA_EN
#include "MQA_XMOS.h"
#endif
@@ -187,6 +188,18 @@ static inline unsigned c1_mode_to_tile_mode_led_code(unsigned mode_value)
void switch_mode_by_c1_mode(unsigned c1_mode, unsigned force_reboot)
{
#if C1_DFS_EN
/* 动态降频 / c1_no_mode_switch: FPS on/off is a runtime toggle (no reboot,
* no role-switch flag). tile[1] clock follows via c1_tile1_power_tick().
* c1_mode 4 = FPS (g_3d_fps=1); otherwise BYPASS (g_3d_fps=0). */
(void)force_reboot;
{
unsigned fps_val = (c1_mode == 4) ? 1u : 0u;
SET_SHARED_GLOBAL(g_3d_fps, fps_val);
debug_printf("switch_mode_by_c1_mode: c1_mode=%d -> g_3d_fps=%d (no reboot)\n", c1_mode, fps_val);
}
return;
#else
unsigned reboot_need = 0;
#if UAC1_MODE == 0
switch (c1_mode)
@@ -248,9 +261,10 @@ void switch_mode_by_c1_mode(unsigned c1_mode, unsigned force_reboot)
if (reboot_need || force_reboot)
{
delay_milliseconds(200);
device_reboot();
while (1);
//device_reboot();
// while (1);
}
#endif /* !C1_DFS_EN: original image-swap + reboot path (factory / non-FPS) */
}
#define NAU88L21_PGA_GAIN_REG_MIN_USED_VALUE 0x0 // 0x1=1, 0dB (0x0, -1dB which is not used in this design)
@@ -439,6 +453,7 @@ uint8_t samp_support(unsigned samFreq)
void dac_volume(signed level, client interface i2c_master_if i2c)
{
// 1dB/步: level 范围 -28 ~ 0对应寄存器 0xcf-28=0xb3 ~ 0xcf
@@ -1465,6 +1480,10 @@ void AudioHwRemote2(streaming chanend c, client interface i2c_master_if i2c, cli
g_last_game_mode = current_game_mode;
#if C1_DFS_EN
/* 动态降频: evaluate tile[1] power state each periodic tick. */
c1_tile1_power_tick();
#endif
}
#endif
@@ -1974,6 +1993,11 @@ void app_control_slave(server interface c1_led_ctrl_if i_c1_led_ctrl)
unsigned mic_mute_switch = 1;
c1_panel_leds_force_all_off_hw();
#if C1_DFS_EN
/* 动态降频: enable tile[1] core divider output path (local setps; must run on tile[1]). */
c1_tile1_clk_enable();
#endif
unsigned eq_mode_time = 0;
timer eq_mode_timer;
eq_mode_timer :> eq_mode_time;

View File

@@ -5,13 +5,20 @@
#include "gpio_access.h"
#include "xc_ptr.h"
#include "debug_print.h"
#include "tile1_clk.h"
extern unsigned g_mute_enable, g_unmute_time;
extern unsigned g_unmute_dac_state;
#if C1_DFS_EN
extern unsigned g_audio_streaming; /* tile1_clk.xc: 1 while USB audio streaming */
#endif
unsigned long get_reference_time();
void UserAudioStreamStart(void)
{
unsigned long time = get_reference_time();
#if C1_DFS_EN
SET_SHARED_GLOBAL(g_audio_streaming, 1);
#endif
SET_SHARED_GLOBAL(g_unmute_dac_state, 1);
SET_SHARED_GLOBAL(g_unmute_time, time);
debug_printf("UserAudioStreamStart\n");
@@ -19,6 +26,9 @@ void UserAudioStreamStart(void)
void UserAudioStreamStop(void)
{
#if C1_DFS_EN
SET_SHARED_GLOBAL(g_audio_streaming, 0);
#endif
unsigned enable_mute;
debug_printf("UserAudioStreamStop\n");
//SET_SHARED_GLOBAL(g_unmute_dac_state, 0);

View File

@@ -19,6 +19,10 @@
void device_reboot(void);
extern void SetRoleSwitchFlag(unsigned mode);
extern unsigned g_3d_fps;
#include "tile1_clk.h"
#if C1_DFS_EN
extern unsigned g_tile1_lp_div; /* tile1_clk.xc: tile[1] low-power divider (动态降频) */
#endif
#if DEBUG_MEMORY_LOG_ENABLED
unsigned g_log_switch = 1;
#else
@@ -148,6 +152,9 @@ static struct {
bool is_get_mute_switch_request; // 改动原因添加获取静音开关请求标志用于GET_MUTE_SWITCH (0xB2)值由MCU通过UART 0x5F返回
bool is_get_listen_switch_request; // 改动原因添加获取监听开关请求标志用于GET_LISTEN_SWITCH (0xB4),直接读 g_adc_loop
bool is_get_dnr_enable_request; // 改动原因添加获取AI通话降噪请求标志用于GET_DNR_ENABLE (0xB5),直接读 g_dnr_enable
#if C1_DFS_EN
bool is_get_tile1_div_request; // 改动原因动态降频获取tile1分频值请求标志用于GET_TILE1_DIV (0xB7)
#endif
int32_t post_gain_db;
} read_request = {0};
@@ -1534,6 +1541,26 @@ unsigned char process_send_params(uint8_t data[], uint16_t len) {
return true;
}
#if C1_DFS_EN
// 处理设置tile1动态降频分频值命令 (0xB6) - SET_TILE1_DIV
// 改动原因动态降频主机设置tile1低功耗分频值(600MHz/N);状态机下次降频或当前已降频时生效
if (data[1] == 0xB6) {
uint8_t div = data[2];
if (div == 0) div = 1; // 防止除0最小分频1全速
SET_SHARED_GLOBAL(g_tile1_lp_div, div);
debug_printf("Received SET_TILE1_DIV (0xB6), g_tile1_lp_div=%u\n", div);
return true;
}
// 处理获取tile1动态降频分频值命令 (0xB7) - GET_TILE1_DIV
// 改动原因:动态降频,返回当前 g_tile1_lp_div
if (data[1] == 0xB7) {
debug_printf("Received get tile1 divider command (GET_TILE1_DIV) via HID 0xB7\n");
read_request.is_get_tile1_div_request = true;
return true;
}
#endif
// 处理获取当前UAC模式命令 (0x9C) - GET_CURRENT_UAC_MODE
// 改动原因添加当前UAC模式查询命令返回当前UAC模式值和名称
if (data[1] == 0x9C) {
@@ -3170,6 +3197,22 @@ unsigned char process_read_params(uint8_t response[]) {
return true;
}
#if C1_DFS_EN
// 处理获取tile1动态降频分频值请求 (0xB7) - GET_TILE1_DIV
// 改动原因:动态降频,返回当前 g_tile1_lp_div600MHz/N 的 N 值)
if (read_request.is_get_tile1_div_request == true) {
unsigned tile1_div;
GET_SHARED_GLOBAL(tile1_div, g_tile1_lp_div);
response[0] = 0x77;
response[1] = 0xB7;
response[2] = (uint8_t)(tile1_div & 0xFF);
for (int i = 3; i < 63; i++) response[i] = 0x00;
read_request.is_get_tile1_div_request = false;
debug_printf("Building 0xB7 response (GET_TILE1_DIV), g_tile1_lp_div=%u\n", tile1_div);
return true;
}
#endif
if (read_request.is_read_request == true) {
#if 1 // 改动原因fosi_c1 使用 -DUAC1=1 编译,原 #if !UAC1 导致 0x8E 读EQ参数在 HID GET_REPORT 时永不组包eq_designer 收到全零响应
debug_printf("Read request information:\n");

View File

@@ -710,6 +710,31 @@ class EQDesigner(QMainWindow):
left_content_layout.addWidget(device_mode_group)
# 添加动态降频控制组HID 0xB6 设置 / 0xB7 获取 tile1 低功耗分频值,仅 FPS 镜像 C1_DFS_EN 生效)
dfs_group = QGroupBox("动态降频 (tile1分频)")
self.ui_groups['dfs'] = dfs_group
dfs_layout = QFormLayout(dfs_group)
dfs_control_layout = QHBoxLayout()
self.dfs_div_label = QLabel("分频值N:")
self.dfs_div_spin = QSpinBox()
self.dfs_div_spin.setRange(1, 255)
self.dfs_div_spin.setValue(40) # 默认40约15MHz与固件 C1_TILE1_LP_XCORE_DIV_DEFAULT 一致)
self.dfs_div_spin.setSuffix(" (600MHz/N)")
dfs_control_layout.addWidget(self.dfs_div_label)
dfs_control_layout.addWidget(self.dfs_div_spin)
dfs_layout.addRow(dfs_control_layout)
self.current_dfs_label = QLabel("当前分频: 未获取")
dfs_layout.addRow("当前分频:", self.current_dfs_label)
dfs_btn_layout = QHBoxLayout()
self.set_dfs_btn = QPushButton("设置分频")
self.set_dfs_btn.clicked.connect(self.on_set_tile1_div)
self.get_dfs_btn = QPushButton("读取分频")
self.get_dfs_btn.clicked.connect(self.on_get_tile1_div)
dfs_btn_layout.addWidget(self.set_dfs_btn)
dfs_btn_layout.addWidget(self.get_dfs_btn)
dfs_layout.addRow(dfs_btn_layout)
left_content_layout.addWidget(dfs_group)
# Log控制组0x70 SET_LOG_SWITCH0=关闭 1=开启),便于在视图菜单中显示/隐藏
log_group = QGroupBox("Log控制")
self.ui_groups['log_switch'] = log_group
@@ -1073,7 +1098,8 @@ class EQDesigner(QMainWindow):
'mute_switch': '静音开关',
'listen_switch': '监听开关',
'eq_enable': 'EQ使能控制',
'firmware': '固件升级'
'firmware': '固件升级',
'dfs': '动态降频'
}
# 为每组创建切换可见性的动作
@@ -1109,6 +1135,8 @@ class EQDesigner(QMainWindow):
'firmware': '固件升级'
}
name = group_names.get(group_key, group_key)
if name == group_key and group_key == 'dfs':
name = '动态降频' # 动态降频组未在上方 dict 中,补回中文名
self.view_actions[group_key].setText(f"{'显示' if visible else '隐藏'} {name}")
def create_initial_filter(self):
@@ -1260,6 +1288,58 @@ class EQDesigner(QMainWindow):
h.close()
def on_set_tile1_div(self):
"""设置tile1动态降频分频值发送0xB6命令"""
if self.device_combo.currentData() is None:
log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
return
div = self.dfs_div_spin.value()
device_info = self.device_combo.currentData()
try:
h = hid.device()
h.open(device_info['vendor_id'], device_info['product_id'])
h.set_nonblocking(1)
data = bytearray(63)
data[0] = 0x77 # 同步头1
data[1] = 0xB6 # 命令码 SET_TILE1_DIV
data[2] = div & 0xFF
log_message(LOG_LEVEL_INFO, f"设置 tile1 动态降频分频值 N={div} (约 {600.0/div:.2f} MHz)", self.log_level)
h.write([0x01] + list(data))
h.close()
except Exception as e:
log_message(LOG_LEVEL_ERROR, f"设置分频时出错: {str(e)}", self.log_level)
def on_get_tile1_div(self):
"""获取tile1动态降频分频值发送0xB7命令并读取响应"""
if self.device_combo.currentData() is None:
log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
return
device_info = self.device_combo.currentData()
try:
h = hid.device()
h.open(device_info['vendor_id'], device_info['product_id'])
h.set_nonblocking(1)
data = bytearray(63)
data[0] = 0x77 # 同步头1
data[1] = 0xB7 # 命令码 GET_TILE1_DIV
h.write([0x01] + list(data))
import time
time.sleep(0.05) # 等待设备响应
reply = h.get_input_report(0x1, 64)
h.close()
if reply and len(reply) == 64 and reply[0] == 0x01 and reply[1] == 0x77 and reply[2] == 0xB7:
div = reply[3]
self.dfs_div_spin.setValue(div if div > 0 else 1)
if div > 0:
self.current_dfs_label.setText(f"当前分频: {div} (约 {600.0/div:.2f} MHz)")
else:
self.current_dfs_label.setText(f"当前分频: {div}")
log_message(LOG_LEVEL_INFO, f"获取 tile1 分频值 N={div}", self.log_level)
else:
log_message(LOG_LEVEL_ERROR, f"无效的0xB7响应: {reply[:4] if reply else 'None'}", self.log_level)
except Exception as e:
log_message(LOG_LEVEL_ERROR, f"获取分频时出错: {str(e)}", self.log_level)
def update_eq_curve(self):
"""更新EQ响应曲线"""
try:

View File

@@ -42,6 +42,8 @@
| 0xB2 | GET_MUTE_SWITCH | 获取静音开关状态 | 主机→设备 | 读取静音开关状态0=关1=开设备通过串口向MCU请求后返回 |
| 0xB3 | SET_LISTEN_SWITCH | 设置监听开关 | 主机→设备 | 设置监听开关0=关1=开通过UART 0x60透传给MCU |
| 0xB4 | GET_LISTEN_SWITCH | 获取监听开关状态 | 主机→设备 | 读取监听开关状态0=关1=开),直接返回 g_adc_loop 的值,不经过串口 |
| 0xB6 | SET_TILE1_DIV | 设置tile1动态降频分频值 | 主机→设备 | 设置tile1低功耗分频值Ncore clock=600MHz/N仅FPS镜像(C1_DFS_EN)生效tile1空闲时降频 |
| 0xB7 | GET_TILE1_DIV | 获取tile1动态降频分频值 | 主机→设备 | 读取当前tile1低功耗分频值N动态降频仅FPS镜像(C1_DFS_EN)生效 |
### 1.3 数据包格式
- **Report ID**: 0x01 (HID报告ID)
@@ -981,6 +983,54 @@
**状态变化上报**: 当监听开关状态g_adc_loop发生变化时设备会主动通过 HID 上报数据格式与上述响应一致0x77 0xB4 + 1 字节监听状态与音量变化上报0x94方式相同主机可通过 HID 读或中断 IN 获取。
### 2.35 0xB6 - SET_TILE1_DIV (设置tile1动态降频分频值)
**功能**: 设置 tile[1] 动态降频(动态降频)的低功耗分频值 N。tile[1] core clock = 600 MHz / N。
**方向**: 主机→设备
**数据包格式**:
```
字节位置 | 长度 | 内容 | 描述
---------|------|------|------
0 | 1 | 0x77 | 同步头1
1 | 1 | 0xB6 | 命令码
2 | 1 | uint8 | 分频值 N (1-2550视为1N=1为全速600MHzN=40约15MHz)
3-62 | 60 | 0x00 | 保留字节
```
**设备端处理**:
- 仅在 FPS 镜像(编译宏 `C1_DFS_EN=1`,默认等于 `XMOS_FPS_EN`)生效;其它镜像(如 factory忽略此命令。
- 写入全局 `g_tile1_lp_div`。tile[1] 电源状态机下次进入 CLOCKED_DOWNFPS 关闭或音频流停止、grace 超时后)时按此分频值降频;若当前已处于 CLOCKED_DOWN则下次状态机 tick 立即按新值重写分频寄存器。
- 全速运行FPS 开启且音频流活跃)时不影响 tile[1] 实时时钟,仅更新待用分频值。
**返回值**:
无直接返回值。如需确认,请使用 GET_TILE1_DIV (0xB7) 读取当前分频值。
### 2.36 0xB7 - GET_TILE1_DIV (获取tile1动态降频分频值)
**功能**: 读取 tile[1] 动态降频当前的低功耗分频值 N。
**方向**: 主机→设备
**请求数据包格式**:
```
字节位置 | 长度 | 内容 | 描述
---------|------|------|------
0 | 1 | 0x77 | 同步头1
1 | 1 | 0xB7 | 命令码
2-62 | 61 | 0x00 | 保留字节
```
**响应数据包格式**:
```
字节位置 | 长度 | 内容 | 描述
---------|------|------|------
0 | 1 | 0x01 | Report ID
1 | 1 | 0x77 | 同步头1
2 | 1 | 0xB7 | 同步头2
3 | 1 | uint8 | 当前分频值 N (1-255)
4-62 | 59 | 0x00 | 保留字节
```
**设备端处理**:
- 仅在 FPS 镜像(`C1_DFS_EN=1`)生效,返回 `g_tile1_lp_div`
- 注意:返回的 N 为"待用/当前低功耗分频值"并非实时核心频率读数tile[1] 是否实际处于降频状态取决于 FPS 开关与音频流状态FPS 关闭或音频停止后才会真正降频)。
## 5. 关键特性
### 5.1 设备端系数计算

View File

@@ -13,6 +13,7 @@
#include "asrc_timestamp_interpolation.h"
#include "debug_print.h"
#include "xc_ptr.h"
#include "tile1_clk.h"
#if BR_UAC2
unsigned g_3d_fps = 2;
@@ -68,6 +69,9 @@ extern void buffer_exchange(chanend c_data, unsigned sampsFromUsbToAudio[], unsi
void dnr_exchange_buffer(int32_t * unsafe data);
extern unsigned g_dnr_enable;
extern unsigned g_adc_loop;
#if C1_DFS_EN
extern unsigned g_tile1_audio_ready; /* tile1_clk.xc: state-machine output */
#endif
#if UAC1
#pragma unsafe arrays
@@ -86,13 +90,20 @@ void UserBufferManagement(unsigned sampsFromUsbToAudio[], unsigned sampsFromAudi
unsigned tmp = sampsFromUsbToAudio[0];
unsigned tile1_ready = 1; /* default (no DFS): always send to tile[1] */
#if C1_DFS_EN
GET_SHARED_GLOBAL(tile1_ready, g_tile1_audio_ready);
#endif
if (l_3d_fps == 2)
{
buffer_exchange((chanend) uc_br_data, sampsFromUsbToAudio, sampsFromAudioToUsb, NUM_USB_CHAN_OUT);
if (tile1_ready)
buffer_exchange((chanend) uc_br_data, sampsFromUsbToAudio, sampsFromAudioToUsb, NUM_USB_CHAN_OUT);
}
else if (l_3d_fps == 1)
{
buffer_exchange((chanend)uc_br_data, sampsFromUsbToAudio, sampsFromAudioToUsb, 2);
if (tile1_ready)
buffer_exchange((chanend)uc_br_data, sampsFromUsbToAudio, sampsFromAudioToUsb, 2);
}
if (dnr_enable)

View File

@@ -11,8 +11,12 @@
#define EP0_MAX_REQUEST_SIZE 2048
#include "xud.h"
#include "vendorrequests.h"
#include "tile1_clk.h"
unsigned g_host_os = 0; // 1 -> Windows, 0 -> Others
#if C1_DFS_EN
extern unsigned g_tile1_lp_div; /* tile1_clk.xc: tile[1] low-power divider */
#endif
int VendorAudioRequests(XUD_ep ep0_out, XUD_ep ep0_in, unsigned char bRequest, unsigned char cs, unsigned char cn,
@@ -36,6 +40,15 @@ int VendorRequests(XUD_ep ep0_out, XUD_ep ep0_in, REFERENCE_PARAM(USB_SetupPacke
//SET_SHARED_GLOBAL(g_host_os, k);
switch ((sp->bmRequestType.Direction << 7) | (sp->bmRequestType.Type << 5) | (sp->bmRequestType.Recipient)) {
case USB_BMREQ_H2D_VENDOR_DEV:
#if C1_DFS_EN
if (sp->bRequest == 0xF0) { /* SET_TILE1_DIV: wValue = divider N */
unsigned div = sp->wValue;
SET_SHARED_GLOBAL(g_tile1_lp_div, div);
/* Applied on next power tick (or immediately while CLOCKED_DOWN). */
result = XUD_DoSetRequestStatus(ep0_in);
return result;
}
#endif
result = XUD_GetBuffer(ep0_out, request_data, len);
if (result == XUD_RES_OKAY) {
if (a/*control_process_usb_set_request(sp.wIndex, sp.wValue, sp.wLength, request_data, i_control) == CONTROL_SUCCESS*/) {
@@ -50,6 +63,16 @@ int VendorRequests(XUD_ep ep0_out, XUD_ep ep0_in, REFERENCE_PARAM(USB_SetupPacke
break;
case USB_BMREQ_D2H_VENDOR_DEV:
#if C1_DFS_EN
if (sp->bRequest == 0xF1) { /* GET_TILE1_DIV -> returns current N */
unsigned div;
GET_SHARED_GLOBAL(div, g_tile1_lp_div);
request_data[0] = (unsigned char)div;
request_data[1] = (unsigned char)(div >> 8);
result = XUD_DoGetRequest(ep0_out, ep0_in, request_data, 2, sp->wLength);
return result;
}
#endif
/* application retrieval latency inside the control library call
* XUD task defers further calls by NAKing USB transactions
*/

View File

@@ -1,24 +0,0 @@
#include <platform.h>
void power_down()
{
#if (LOW_POWER_EN == 1)
// Give the software 10 seconds to start up, then apply power optimisations below.
timer tmr;
int t;
tmr :> t;
tmr when timerafter(t+1000000000) :> void;
// Reduce switch clock frequency
// write_node_config_reg(tile[0], XS1_SSWITCH_CLK_DIVIDER_NUM, 4);
write_node_config_reg(tile[1], XS1_SSWITCH_CLK_DIVIDER_NUM, 4);
//
// // Reduce core 0 clock frequency (to 9 MHz)
// // Note, to completely disable, use:
// // write_tile_config_reg(tile[0], XS1_PSWITCH_PLL_CLK_DIVIDER_NUM, 0x80000000);
// write_tile_config_reg(tile[0], XS1_PSWITCH_PLL_CLK_DIVIDER_NUM, 0x00000040);
write_tile_config_reg(tile[1], XS1_PSWITCH_PLL_CLK_DIVIDER_NUM, 0x00000040);
setps(XS1_PS_XCORE_CTRL0, 0x10);
#endif
}

View File

@@ -0,0 +1,60 @@
#ifndef _TILE1_CLK_H_
#define _TILE1_CLK_H_
/* =====================================================================
* tile1_clk.h -- tile[1] dynamic frequency scaling (动态降频)
*
* Divides tile[1]'s core PLL clock down when the tile is idle (FPS off or
* audio not streaming) and restores full speed before audio flows back to
* tile[1]. Ported from ds1 power_down.xc.
*
* Master enable: C1_DFS_EN. Defaults to the FPS build only (XMOS_FPS_EN),
* so factory / other targets compile nothing and are unaffected.
* Override on the command line (-DC1_DFS_EN=0/1) if needed.
* ===================================================================== */
#ifndef C1_DFS_EN
# ifdef XMOS_FPS_EN
# define C1_DFS_EN XMOS_FPS_EN
# else
# define C1_DFS_EN 0
# endif
#endif
#if C1_DFS_EN
/* Default low-power divider for tile[1] core clock when idle.
* The register is programmed with (value - 1); result = 600 MHz / N.
* 40 -> ~15 MHz (ds1-proven start point). Sweep lower via SET_TILE1_DIV. */
#ifndef C1_TILE1_LP_XCORE_DIV_DEFAULT
#define C1_TILE1_LP_XCORE_DIV_DEFAULT 40
#endif
/* ~100 us settle after restoring full speed (100 MHz timer ticks). */
#define C1_TILE1_CLK_SETTLE_TICKS 10000
/* Grace period before clocking down once tile[1] is unneeded (~1000 ms). */
#define C1_TILE1_SHUTDOWN_GRACE_TICKS 100000000
/* Power state machine states. */
#define C1_TILE1_ACTIVE 0
#define C1_TILE1_PENDING_SHUTDOWN 1
#define C1_TILE1_CLOCKED_DOWN 2
/* One-time enable of the tile[1] core divider output path. MUST run on tile[1]. */
void c1_tile1_clk_enable(void);
/* Restore tile[1] to full speed (divider = 1). Cross-tile safe (run from tile[0]). */
void c1_tile1_clock_up(void);
/* Throttle tile[1] core clock to 600 MHz / div. Cross-tile safe. */
void c1_tile1_clock_down(unsigned div);
/* Periodic power-policy tick (call from AudioHwRemote2 periodic loop, tile[0]).
* Owns the ACTIVE/PENDING_SHUTDOWN/CLOCKED_DOWN machine and the
* g_tile1_audio_ready gate that UserBufferManagement checks before sending
* audio to tile[1]. */
void c1_tile1_power_tick(void);
#endif /* C1_DFS_EN */
#endif /* _TILE1_CLK_H_ */

View File

@@ -0,0 +1,113 @@
/* =====================================================================
* tile1_clk.xc -- tile[1] dynamic frequency scaling (动态降频)
* Ported from ds1 power_down.xc; guarded by C1_DFS_EN (FPS build only).
* In non-FPS builds this translation unit compiles to nothing.
* ===================================================================== */
#include <platform.h>
#include <xs1.h>
#include "xua.h"
#include "xc_ptr.h"
#include "tile1_clk.h"
#if C1_DFS_EN
/* ---- policy globals (tile[0]); accessed elsewhere via GET/SET_SHARED_GLOBAL ---- */
unsigned g_audio_streaming = 0; /* 1 while USB audio is streaming */
unsigned g_tile1_audio_ready = 0; /* state-machine output: 1 => tile[0] may send to tile[1] */
unsigned g_tile1_lp_div = C1_TILE1_LP_XCORE_DIV_DEFAULT;
extern unsigned g_3d_fps; /* defined in extra_i2s.xc: 0=bypass, 1=FPS */
/* ---- state (tile[0]) ---- */
static unsigned c1_tile1_state = C1_TILE1_ACTIVE;
static timer c1_tile1_tmr;
static unsigned c1_tile1_grace_target = 0;
static int c1_tile1_grace_armed = 0;
/* ---- clock primitives (mirror ds1 enable_tile1_core_divider / up / down) ---- */
/* Local to tile[1]: uses per-core setps/getps. */
void c1_tile1_clk_enable(void)
{
/* Program divider = 1 (full speed), then enable the divider output path
* via XCORE_CTRL0 bit 4. */
write_pswitch_reg(get_tile_id(tile[1]), XS1_PSWITCH_PLL_CLK_DIVIDER_NUM, 1 - 1);
unsigned val = getps(XS1_PS_XCORE_CTRL0);
setps(XS1_PS_XCORE_CTRL0, val | (1u << 4));
}
void c1_tile1_clock_up(void)
{
write_pswitch_reg(get_tile_id(tile[1]), XS1_PSWITCH_PLL_CLK_DIVIDER_NUM, 1 - 1); /* /1 => 600 MHz */
}
void c1_tile1_clock_down(unsigned div)
{
if (div == 0) div = 1;
write_pswitch_reg(get_tile_id(tile[1]), XS1_PSWITCH_PLL_CLK_DIVIDER_NUM, div - 1);
}
/* ---- policy tick ----
* Invariants:
* - restore-before-send: clock_up + settle BEFORE g_tile1_audio_ready = 1.
* - bypass-before-slow: g_tile1_audio_ready = 0 BEFORE clock_down. */
void c1_tile1_power_tick(void)
{
unsigned fps, aud_stream;
GET_SHARED_GLOBAL(fps, g_3d_fps);
GET_SHARED_GLOBAL(aud_stream, g_audio_streaming);
int tile1_needed = (fps != 0) && (aud_stream != 0);
switch (c1_tile1_state)
{
case C1_TILE1_ACTIVE:
if (!tile1_needed)
{
SET_SHARED_GLOBAL(g_tile1_audio_ready, 0); /* bypass-before-slow */
unsigned now; c1_tile1_tmr :> now;
c1_tile1_grace_target = now + C1_TILE1_SHUTDOWN_GRACE_TICKS;
c1_tile1_grace_armed = 1;
c1_tile1_state = C1_TILE1_PENDING_SHUTDOWN;
}
break;
case C1_TILE1_PENDING_SHUTDOWN:
if (tile1_needed)
{
/* Clock still up; just re-arm audio. */
c1_tile1_grace_armed = 0;
SET_SHARED_GLOBAL(g_tile1_audio_ready, 1);
c1_tile1_state = C1_TILE1_ACTIVE;
}
else if (c1_tile1_grace_armed)
{
unsigned now; c1_tile1_tmr :> now;
if ((int)(now - c1_tile1_grace_target) >= 0)
{
c1_tile1_grace_armed = 0;
c1_tile1_clock_down(g_tile1_lp_div);
c1_tile1_state = C1_TILE1_CLOCKED_DOWN;
}
}
break;
case C1_TILE1_CLOCKED_DOWN:
if (tile1_needed)
{
c1_tile1_clock_up(); /* restore-before-send */
unsigned t; c1_tile1_tmr :> t;
c1_tile1_tmr when timerafter(t + C1_TILE1_CLK_SETTLE_TICKS) :> void;
SET_SHARED_GLOBAL(g_tile1_audio_ready, 1);
c1_tile1_state = C1_TILE1_ACTIVE;
}
else
{
/* Apply a live divider change (SET_TILE1_DIV) while staying clocked down. */
c1_tile1_clock_down(g_tile1_lp_div);
}
break;
}
}
#endif /* C1_DFS_EN */