固件 0.1.4-c5:整片刀片不应答(INA226 + PCA9555 同时 NAK 200 ms)⇒ 关这一路
- t_safety:eFuse 开着、本路两颗都 NAK 连续 10 圈 ⇒ abort + 关 eFuse,新事件 20 BLADE_MUTE; 只关 eFuse,不再写刚 NAK 过的 PCA9555。只有一颗不答应仍按 §10.10 盲跑、保持供电 - 起因:板 B 第 2 槽 PRSNT2# 卡低,刀片挪走后空槽 eFuse 开了约 4 h,原判据一条没响 - 需求 §10.10 加一行 + 表后第 3 条;rack 事件 20 命名并标红 - 宿主机 26 ctest 全绿(test_t_safety_rules +4 例) Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
@@ -87,7 +87,8 @@ static void test_flash_format_is_frozen(void)
|
||||
TEST_ASSERT_EQUAL_INT(17, LOG_EV_PG_TIMEOUT); /* A15 */
|
||||
TEST_ASSERT_EQUAL_INT(18, LOG_EV_OTA); /* C2 */
|
||||
TEST_ASSERT_EQUAL_INT(19, LOG_EV_BOOT_CHECK); /* 0.1.2 开机校验 */
|
||||
TEST_ASSERT_EQUAL_INT(20, LOG_EV_COUNT);
|
||||
TEST_ASSERT_EQUAL_INT(20, LOG_EV_BLADE_MUTE); /* 0.1.4 刀片不应答 */
|
||||
TEST_ASSERT_EQUAL_INT(21, LOG_EV_COUNT);
|
||||
}
|
||||
|
||||
/* §10.7 的「必记事件」有八类,A10 只产生其中两类。剩下的号先占住,
|
||||
@@ -129,6 +130,7 @@ static void test_the_safety_mapping_is_explicit(void)
|
||||
TEST_ASSERT_EQUAL_INT(LOG_EV_UNDER_VOLT, evlog_from_safety(SAFETY_EV_UNDER_VOLT));
|
||||
TEST_ASSERT_EQUAL_INT(LOG_EV_REVERSE, evlog_from_safety(SAFETY_EV_REVERSE));
|
||||
TEST_ASSERT_EQUAL_INT(LOG_EV_PUSHED_UP, evlog_from_safety(SAFETY_EV_PUSHED_UP));
|
||||
TEST_ASSERT_EQUAL_INT(LOG_EV_BLADE_MUTE, evlog_from_safety(SAFETY_EV_BLADE_MUTE));
|
||||
TEST_ASSERT_EQUAL_INT(LOG_EV_NONE, evlog_from_safety(SAFETY_EV_NONE));
|
||||
TEST_ASSERT_EQUAL_INT(LOG_EV_NONE, evlog_from_safety(200)); /* 认不出的不许瞎猜 */
|
||||
|
||||
@@ -161,6 +163,7 @@ static void test_the_safety_mapping_is_a_table_not_an_offset(void)
|
||||
TEST_ASSERT_EQUAL_INT(4, SAFETY_EV_PUSHED_UP);
|
||||
TEST_ASSERT_EQUAL_INT(5, SAFETY_EV_MUX_LOST);
|
||||
TEST_ASSERT_EQUAL_INT(6, SAFETY_EV_SLOT_EMPTY);
|
||||
TEST_ASSERT_EQUAL_INT(7, SAFETY_EV_BLADE_MUTE);
|
||||
|
||||
/* 逐对:safety 号 → flash 号,两边都是裸数字。 */
|
||||
TEST_ASSERT_EQUAL_INT(0, (int)evlog_from_safety(0)); /* NONE */
|
||||
@@ -170,6 +173,7 @@ static void test_the_safety_mapping_is_a_table_not_an_offset(void)
|
||||
TEST_ASSERT_EQUAL_INT(6, (int)evlog_from_safety(4)); /* PUSHED_UP */
|
||||
TEST_ASSERT_EQUAL_INT(14, (int)evlog_from_safety(5)); /* MUX_LOST */
|
||||
TEST_ASSERT_EQUAL_INT(15, (int)evlog_from_safety(6)); /* SLOT_EMPTY */
|
||||
TEST_ASSERT_EQUAL_INT(20, (int)evlog_from_safety(7)); /* BLADE_MUTE(0.1.4) */
|
||||
|
||||
/* 而且这张表**在数学上不可能**是一个偏移:前四对差 2,后两对差 9。
|
||||
写死这一条,是为了让「改成偏移」这件事在用例名上就说得清楚,
|
||||
@@ -178,8 +182,8 @@ static void test_the_safety_mapping_is_a_table_not_an_offset(void)
|
||||
TEST_ASSERT_EQUAL_INT(9, (int)evlog_from_safety(5) - (int)SAFETY_EV_MUX_LOST);
|
||||
|
||||
/* 越过表尾的号一律 NONE。偏移写法在这里会算出一个**越界但合法**的
|
||||
`log_ev_t`(7 → 记成 eFuse 故障,200 → 记成 202 这种根本没有的号)。 */
|
||||
TEST_ASSERT_EQUAL_INT(0, (int)evlog_from_safety(7));
|
||||
`log_ev_t`(8 → 记成 PG 变化,200 → 记成 202 这种根本没有的号)。 */
|
||||
TEST_ASSERT_EQUAL_INT(0, (int)evlog_from_safety(8));
|
||||
TEST_ASSERT_EQUAL_INT(0, (int)evlog_from_safety(200));
|
||||
TEST_ASSERT_EQUAL_INT(0, (int)evlog_from_safety(255));
|
||||
}
|
||||
|
||||
@@ -133,6 +133,7 @@ static void catch_event(void *ctx, const safety_event_t *ev)
|
||||
case SAFETY_EV_OVER_VOLT:
|
||||
case SAFETY_EV_UNDER_VOLT:
|
||||
case SAFETY_EV_SLOT_EMPTY:
|
||||
case SAFETY_EV_BLADE_MUTE:
|
||||
if (rail_efuse_is_on(ev->rail)) {
|
||||
g_ev_before_action = true; /* 记这条时电早该断了 */
|
||||
}
|
||||
@@ -336,6 +337,11 @@ static void test_thresholds_match_the_documents(void)
|
||||
已经落盘的旧记录改变含义(`t_safety.h` 那句「flash 格式」)。 */
|
||||
TEST_ASSERT_EQUAL_INT(SAFETY_EV_PUSHED_UP + 1, SAFETY_EV_MUX_LOST);
|
||||
TEST_ASSERT_EQUAL_INT(SAFETY_EV_MUX_LOST + 1, SAFETY_EV_SLOT_EMPTY);
|
||||
TEST_ASSERT_EQUAL_INT(SAFETY_EV_SLOT_EMPTY + 1, SAFETY_EV_BLADE_MUTE);
|
||||
|
||||
/* 0.1.4 刀片不应答:连续 10 圈 = 200 ms。必须 > 1(一圈毛刺不许关一台手机)。 */
|
||||
TEST_ASSERT_EQUAL_INT(10, T_SAFETY_BLADE_MUTE_N);
|
||||
TEST_ASSERT_EQUAL_UINT32(200u, T_SAFETY_BLADE_MUTE_N * task_desc(TASK_SAFETY)->period_ms);
|
||||
}
|
||||
|
||||
/* ---- 验收主项一:注入过压 → 20 ms 内关路并记事件(SR-5) ----------------- */
|
||||
@@ -547,6 +553,82 @@ static void test_a_blind_rail_keeps_its_power(void)
|
||||
TEST_ASSERT_EQUAL_INT(0, en_level(4));
|
||||
}
|
||||
|
||||
/* ---- 0.1.4:整片刀片不应答 ⇒ 关路(§10.10 新增那一行) ------------------ */
|
||||
|
||||
static void mute_blade(int rail, bool mute)
|
||||
{
|
||||
sim_i2c_set_present_ch(ch_of(rail), rail_cfg(rail)->ina_addr, !mute);
|
||||
sim_i2c_set_present_ch(ch_of(rail), PCA9555_ADDR, !mute);
|
||||
}
|
||||
|
||||
/* 2026-09-28 板 B 第 2 槽的现场:`PRSNT#` 卡低(假在位)、刀片已经拔走、eFuse 还开着。
|
||||
两颗都不答应满 10 圈 ⇒ 关这一路、记一条 BLADE_MUTE;别的路一根线都不动。 */
|
||||
static void test_a_mute_blade_is_cut_after_ten_cycles(void)
|
||||
{
|
||||
power_up(2);
|
||||
power_up(3);
|
||||
const uint32_t other = en_writes(3);
|
||||
const uint32_t out0 = g_w[1].n;
|
||||
mute_blade(2, true); /* PRSNT2# 仍是低:slot_present(2) 照旧为真 */
|
||||
|
||||
beats(T_SAFETY_BLADE_MUTE_N - 1);
|
||||
TEST_ASSERT_EQUAL_INT(1, en_level(2)); /* 9 圈:还没到 */
|
||||
TEST_ASSERT_EQUAL_INT(0, g_n_ev);
|
||||
beat();
|
||||
TEST_ASSERT_EQUAL_INT(0, en_level(2)); /* 第 10 圈:关 */
|
||||
TEST_ASSERT_FALSE(rail_efuse_is_on(2));
|
||||
TEST_ASSERT_EQUAL_INT(1, g_n_ev);
|
||||
TEST_ASSERT_EQUAL_INT(SAFETY_EV_BLADE_MUTE, g_ev[0].type);
|
||||
TEST_ASSERT_EQUAL_INT(2, g_ev[0].rail);
|
||||
/* 没有去写那片刚 NAK 过的 PCA9555(断 VBUS 那一写只会白等超时) */
|
||||
TEST_ASSERT_EQUAL_UINT32(out0, g_w[1].n);
|
||||
/* 第 3 路一根线都没动 */
|
||||
TEST_ASSERT_EQUAL_INT(1, en_level(3));
|
||||
TEST_ASSERT_EQUAL_UINT32(other, en_writes(3));
|
||||
|
||||
beats(50); /* 关掉之后不再刷事件 */
|
||||
TEST_ASSERT_EQUAL_INT(1, g_n_ev);
|
||||
}
|
||||
|
||||
/* 只差一圈就恢复了:从头数,不关。 */
|
||||
static void test_a_blade_that_answers_again_restarts_the_count(void)
|
||||
{
|
||||
power_up(2);
|
||||
mute_blade(2, true);
|
||||
beats(T_SAFETY_BLADE_MUTE_N - 1);
|
||||
mute_blade(2, false);
|
||||
beat();
|
||||
mute_blade(2, true);
|
||||
beats(T_SAFETY_BLADE_MUTE_N - 1);
|
||||
TEST_ASSERT_EQUAL_INT(1, en_level(2));
|
||||
TEST_ASSERT_EQUAL_INT(0, g_n_ev);
|
||||
beat();
|
||||
TEST_ASSERT_EQUAL_INT(0, en_level(2));
|
||||
}
|
||||
|
||||
/* §10.10 原有的两行不变:只有一颗不答应 ⇒ 盲跑、保持供电。 */
|
||||
static void test_one_mute_chip_on_the_blade_still_keeps_power(void)
|
||||
{
|
||||
power_up(2);
|
||||
sim_i2c_set_present_ch(ch_of(2), PCA9555_ADDR, false); /* 只有 PCA9555 不答应 */
|
||||
beats(50);
|
||||
TEST_ASSERT_EQUAL_INT(1, en_level(2));
|
||||
sim_i2c_set_present_ch(ch_of(2), PCA9555_ADDR, true);
|
||||
sim_i2c_set_present_ch(ch_of(2), rail_cfg(2)->ina_addr, false); /* 只有 INA226 不答应 */
|
||||
beats(50);
|
||||
TEST_ASSERT_EQUAL_INT(1, en_level(2));
|
||||
TEST_ASSERT_EQUAL_INT(0, g_n_ev);
|
||||
}
|
||||
|
||||
/* 关着的路不数:空槽假在位、没人开它,不该每 200 ms 刷一条事件。 */
|
||||
static void test_a_mute_blade_that_is_off_is_left_alone(void)
|
||||
{
|
||||
mute_blade(2, true);
|
||||
beats(50);
|
||||
TEST_ASSERT_EQUAL_INT(0, en_level(2));
|
||||
TEST_ASSERT_EQUAL_INT(0, g_n_ev);
|
||||
}
|
||||
|
||||
/* ---- ⭐ 关路这件事本身:R2 的顺序与「写给了哪一片」 ---------------------- */
|
||||
|
||||
/* ⭐⭐ 本包最要紧的一条。关路 = **先断 VBUS(锁内、本路通道上)再关 eFuse(锁外)**。
|
||||
@@ -1203,6 +1285,10 @@ int main(void)
|
||||
RUN_TEST(test_a_rail_that_is_off_is_never_judged);
|
||||
RUN_TEST(test_the_soft_start_window_is_not_judged);
|
||||
RUN_TEST(test_a_blind_rail_keeps_its_power);
|
||||
RUN_TEST(test_a_mute_blade_is_cut_after_ten_cycles);
|
||||
RUN_TEST(test_a_blade_that_answers_again_restarts_the_count);
|
||||
RUN_TEST(test_one_mute_chip_on_the_blade_still_keeps_power);
|
||||
RUN_TEST(test_a_mute_blade_that_is_off_is_left_alone);
|
||||
|
||||
RUN_TEST(test_the_cut_drops_vbus_inside_the_lock_on_this_rails_channel);
|
||||
RUN_TEST(test_only_the_offending_rail_is_touched);
|
||||
|
||||
在新工单中引用
屏蔽一个用户