diff --git a/sw_usb_audio/app_usb_aud_fosi_c1/gen_factory.bat b/sw_usb_audio/app_usb_aud_fosi_c1/gen_factory.bat index b76846e..17a29c0 100644 --- a/sw_usb_audio/app_usb_aud_fosi_c1/gen_factory.bat +++ b/sw_usb_audio/app_usb_aud_fosi_c1/gen_factory.bat @@ -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 diff --git a/sw_usb_audio/app_usb_aud_fosi_c1/gen_update.bat b/sw_usb_audio/app_usb_aud_fosi_c1/gen_update.bat index b644bc9..5e231e6 100644 --- a/sw_usb_audio/app_usb_aud_fosi_c1/gen_update.bat +++ b/sw_usb_audio/app_usb_aud_fosi_c1/gen_update.bat @@ -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 diff --git a/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/audiohw.xc b/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/audiohw.xc index ad782ba..259d623 100644 --- a/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/audiohw.xc +++ b/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/audiohw.xc @@ -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; diff --git a/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/audiostream.xc b/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/audiostream.xc index eb09535..eb090c4 100644 --- a/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/audiostream.xc +++ b/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/audiostream.xc @@ -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); diff --git a/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/eq.c b/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/eq.c index 80b309f..1c22f7d 100644 --- a/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/eq.c +++ b/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/eq.c @@ -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_div(600MHz/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"); diff --git a/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/eq_designer_new.py b/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/eq_designer_new.py index 8d06433..a11178f 100644 --- a/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/eq_designer_new.py +++ b/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/eq_designer_new.py @@ -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_SWITCH,0=关闭 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: diff --git a/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/eq_hid_protocol.md b/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/eq_hid_protocol.md index c7771df..43cf461 100644 --- a/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/eq_hid_protocol.md +++ b/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/eq_hid_protocol.md @@ -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低功耗分频值N(core 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-255,0视为1;N=1为全速600MHz,N=40约15MHz) +3-62 | 60 | 0x00 | 保留字节 +``` + +**设备端处理**: +- 仅在 FPS 镜像(编译宏 `C1_DFS_EN=1`,默认等于 `XMOS_FPS_EN`)生效;其它镜像(如 factory)忽略此命令。 +- 写入全局 `g_tile1_lp_div`。tile[1] 电源状态机下次进入 CLOCKED_DOWN(FPS 关闭或音频流停止、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 设备端系数计算 diff --git a/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/extra_i2s.xc b/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/extra_i2s.xc index f69bad4..247e6bc 100644 --- a/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/extra_i2s.xc +++ b/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/extra_i2s.xc @@ -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) diff --git a/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/host_os_detect.c b/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/host_os_detect.c index a6f10d7..8225f22 100644 --- a/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/host_os_detect.c +++ b/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/host_os_detect.c @@ -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 */ diff --git a/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/power_down.xc b/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/power_down.xc deleted file mode 100644 index dfebeb2..0000000 --- a/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/power_down.xc +++ /dev/null @@ -1,24 +0,0 @@ -#include - -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 -} diff --git a/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/tile1_clk.h b/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/tile1_clk.h new file mode 100644 index 0000000..7c2ec10 --- /dev/null +++ b/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/tile1_clk.h @@ -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_ */ diff --git a/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/tile1_clk.xc b/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/tile1_clk.xc new file mode 100644 index 0000000..2011b04 --- /dev/null +++ b/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/tile1_clk.xc @@ -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 +#include +#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 */