fix(pdb): Read the bricks on the pages the datasheet actually specifies

Every PDB reading came back NA, and the cause was a base error. The four
ADPM12200 bricks put each measurement on its own PMBus PAGE, written
before the read. The bring-up spreadsheet listed those page numbers in
decimal but with an 0x prefix:

    Vin  PAGE 9  -> it said 0x00   (0x09)
    Iin  PAGE 10 -> it said 0x10   (0x0A)
    Vout PAGE 2  -> it said 0x02    correct by luck
    Iout PAGE 14 -> it said 0x14   (0x0E)
    Temp PAGE 18 -> it said 0x18   (0x12)

Only Vout worked, because 2 reads the same in either base. Hence Iin and
Iout answering 0xFFFF, Temp answering 0x0000, and a Vin scale factor
having to be invented to make 50 V appear out of a page-0 register.

pwr_brick.sh reads all five bricks with the right pages and the
datasheet's DIRECT equation, X = (1/m)(Y x 10^-R - b): voltage Y x 8 mV,
current Y x 0.04 A, temperature Y x 0.01 C. Cross-checked rather than
assumed - READ_VOUT 0x05D4 decodes to 11936 mV, exactly what
'show platform voltage' reports for that rail through a separate sensor
path. The datasheet is committed alongside so the numbers are checkable.

Left flagged: Table 3 does not list READ_VIN or READ_IIN. They borrow the
voltage and current coefficients here, which is consistent with a ~50 V
input but is not something the datasheet states.

Each value is re-read until it passes three checks: not 0xFFFF, not
0x0000, and inside a plausibility window. The window is the one that
matters - this bus corrupts the HIGH BYTE only, which turns 11.98 V into
32.00 V. Positive, plausible in magnitude, and invisible to any all-ones
filter. 0x0000 is rejected for the mirror-image reason: on the
temperature register it decodes to a believable 0 C.

Adds comboA/comboB margin profiles per board. comboB is comboA with every
direction flipped, so the pair covers each rail high and low while its
neighbours sit the other way. NC channels stay nominal in both - there is
nothing to margin on an unused rail.

Script C re-enables the BMC USB journalctl dump, which had been commented
out, so that service log now reaches the master log.

Script A -> V1.1.6.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014RetWKFZFG1ZHcitQAyhwM
This commit is contained in:
2026-09-02 16:35:45 +08:00
co-authored by Claude Opus 5
parent 215fed6e95
commit 25e1ce0068
17 changed files with 648 additions and 7 deletions
+14 -1
View File
@@ -120,6 +120,7 @@ Blanton_TTL_Script/
│ ├── Blantons_FPGA_Registers_draft.docx # 同上,客戶原始 docx │ ├── Blantons_FPGA_Registers_draft.docx # 同上,客戶原始 docx
│ ├── Blantons_FPGA_Registers_Map_draft.xlsx │ ├── Blantons_FPGA_Registers_Map_draft.xlsx
│ ├── Blantons_PCIe_AER_and_DDR_EDAC_checks.pdf # AER / EDAC 檢查點依據 │ ├── Blantons_PCIe_AER_and_DDR_EDAC_checks.pdf # AER / EDAC 檢查點依據
│ ├── ADPM12200CMLZxx_DS_Datasheet_*.pdf # PDB brick 手冊(PAGE 與 DIRECT 係數的來源)
│ ├── Blanton_Test_Flow.drawio # A/B/C + 熱安規四支的測試流程圖(draw.io) │ ├── Blanton_Test_Flow.drawio # A/B/C + 熱安規四支的測試流程圖(draw.io)
│ ├── LTC2980_channel_map.csv # 9 個 settings/*.conf 合併:Board,CONN,Ch,NetName,Vnom144 列) │ ├── LTC2980_channel_map.csv # 9 個 settings/*.conf 合併:Board,CONN,Ch,NetName,Vnom144 列)
│ ├── TTL_Script_Blanton_Status_20260814.xlsx │ ├── TTL_Script_Blanton_Status_20260814.xlsx
@@ -193,6 +194,7 @@ Blanton_TTL_Script/
├── port_prbs_monitor.sh # 100G uplink PRBS 測試(bcmcmd phy diag ├── port_prbs_monitor.sh # 100G uplink PRBS 測試(bcmcmd phy diag
├── blanton_ber.sh # 全機 SerDes BER 掃描(phydiag prbs / prbsstat ├── blanton_ber.sh # 全機 SerDes BER 掃描(phydiag prbs / prbsstat
├── blanton_multiphase_margin.sh # 多相 VRM margin(原生 i2c38 條軌) ├── blanton_multiphase_margin.sh # 多相 VRM margin(原生 i2c38 條軌)
├── pwr_brick.sh # PDB / CPB power brick 讀值(VI2C ch4
├── nfc_polling.sh # NFC tag 背景輪詢 + PASS/FAIL 統計 ├── nfc_polling.sh # NFC tag 背景輪詢 + PASS/FAIL 統計
├── blantons_nfc_validate.py # NFC/NCI 驗證工具(廠商提供,未修改) ├── blantons_nfc_validate.py # NFC/NCI 驗證工具(廠商提供,未修改)
├── blanton_tr518.sh # TR518 封包測試,量功耗前的負載來源 ├── blanton_tr518.sh # TR518 封包測試,量功耗前的負載來源
@@ -209,6 +211,7 @@ Blanton_TTL_Script/
├── settings/*.conf # 一個 .conf = 一顆 LTC2980CB + SWB0/1 × CONN13~16 ├── settings/*.conf # 一個 .conf = 一顆 LTC2980CB + SWB0/1 × CONN13~16
├── profiles/*.conf # 16-ch 批次組合。SWB 為 per-CONN ├── profiles/*.conf # 16-ch 批次組合。SWB 為 per-CONN
# # combo_SWB_CONN13~16_{high,low}(百分比對應該片的軌) # # combo_SWB_CONN13~16_{high,low}(百分比對應該片的軌)
# # comboA/comboB_{CB,SWB_CONN13~16}(互補棋盤配對)
# # 通用:high3/5、low3/5、normal、all_off # # 通用:high3/5、low3/5、normal、all_off
├── script/*_all.sh # 跨 9 顆 LTC2980 的批次 status / apply / save ├── script/*_all.sh # 跨 9 顆 LTC2980 的批次 status / apply / save
├── Ref/margin_command_trace.md # 指令追蹤參考(SWB 章節已標過期,見文件內警告) ├── Ref/margin_command_trace.md # 指令追蹤參考(SWB 章節已標過期,見文件內警告)
@@ -360,7 +363,7 @@ PROFILE_CHANNELS=( "0:high:8" "1:low:8" ... ) # "channel:operation:change_perc
| 模組 | 進入點 | 功能 | | 模組 | 進入點 | 功能 |
|------|--------|------| |------|--------|------|
| **Script APre-test** | `1_Blanton_Script_A.ttl` V1.1.5 | root 登入 → `date -s` 對時 → 清 job log → **收掉前一輪殘留**`hw-test-session finish``bgctl reset --yes``bgctl stop --all``bgctl list` 確認清空)→ `hw-test-session start`**設定風扇轉速****等資料面就緒**`wait_init.ttl`)→ DUT 清單(boot/version/fwutil/syseeprom/ssdhealth/TPM/NVMe)→ BMC enroll + version/status → source bash 工具 → 清/讀 7 組 PCIe AER + `ras-mc-ctl --summary``lspci` → margin → PMON → dmesg → **10G/1G 管理網路 ping****NFC 基線**start → 30s → stop → report)→ 100G uplink 設定與狀態 → **一輪流量基線**`show uptime` | | **Script APre-test** | `1_Blanton_Script_A.ttl` V1.1.6 | root 登入 → `date -s` 對時 → 清 job log → **收掉前一輪殘留**`hw-test-session finish``bgctl reset --yes``bgctl stop --all``bgctl list` 確認清空)→ `hw-test-session start`**設定風扇轉速****等資料面就緒**`wait_init.ttl`)→ DUT 清單(boot/version/fwutil/syseeprom/ssdhealth/TPM/NVMe)→ BMC enroll + version/status → source bash 工具 → 清/讀 7 組 PCIe AER + `ras-mc-ctl --summary``lspci` → margin → PMON → dmesg → **10G/1G 管理網路 ping****NFC 基線**start → 30s → stop → report)→ 100G uplink 設定與狀態 → **一輪流量基線**`show uptime` |
| **Script BStress** | `2_Blanton_Script_B.ttl` | `mlucas-avx2 -cpu 0:15` + `bgctl run``memtester` / SSD / USB 壓力(v1.1.5 起 SSD/USB `--runtime 86400`、BMC memtester 不帶次數 = 跑到被停) + BMC DDR(`bmc-manager run memtester`)→ BMC USB net test(探測 cdc_ncm 介面,`systemd-run` 掛 4 小時 ping)→ 啟動 mgmt ping 監控 → **啟動 NFC 輪詢**`jobs` / `bgctl list` → traffic(依 `SWB_UNIT`)→ `while 1` 每輪 PMON + `bgctl list`/`jobs` + `pause 60`**V1.1.4 起 margin 不在迴圈內**| | **Script BStress** | `2_Blanton_Script_B.ttl` | `mlucas-avx2 -cpu 0:15` + `bgctl run``memtester` / SSD / USB 壓力(v1.1.5 起 SSD/USB `--runtime 86400`、BMC memtester 不帶次數 = 跑到被停) + BMC DDR(`bmc-manager run memtester`)→ BMC USB net test(探測 cdc_ncm 介面,`systemd-run` 掛 4 小時 ping)→ 啟動 mgmt ping 監控 → **啟動 NFC 輪詢**`jobs` / `bgctl list` → traffic(依 `SWB_UNIT`)→ `while 1` 每輪 PMON + `bgctl list`/`jobs` + `pause 60`**V1.1.4 起 margin 不在迴圈內**|
| **Script CPost-test** | `3_Blanton_Script_C.ttl` | `kill $(jobs -p)` + `bgctl stop --all` / `reset` + 停 BMC USB unit、mgmt ping 與 **NFC 輪詢** → PMON + dmesg → 7 組 AER + `ras-mc-ctl` → BMC USB `journalctl``ip -s link show eth0/eth1``cat` mlucas log → **NFC `report`** → traffic `stop` + `report`**BER 掃描**(依 `SWB_UNIT`)→ NVMe 健康 → `show reboot-cause`**margin 掃描**`EN_Margin`V1.1.4 由 Script B 移來)→ `uptime`**job log 複製到 USB(帶時戳)**`exit` + wait『Script done』→ `hw-test-session finish` | | **Script CPost-test** | `3_Blanton_Script_C.ttl` | `kill $(jobs -p)` + `bgctl stop --all` / `reset` + 停 BMC USB unit、mgmt ping 與 **NFC 輪詢** → PMON + dmesg → 7 組 AER + `ras-mc-ctl` → BMC USB `journalctl``ip -s link show eth0/eth1``cat` mlucas log → **NFC `report`** → traffic `stop` + `report`**BER 掃描**(依 `SWB_UNIT`)→ NVMe 健康 → `show reboot-cause`**margin 掃描**`EN_Margin`V1.1.4 由 Script B 移來)→ `uptime`**job log 複製到 USB(帶時戳)**`exit` + wait『Script done』→ `hw-test-session finish` |
| **Script 4Thermal / Safety** | `4_Blanton_Script_thermal_safety.ttl` | **獨立腳本,不接 A→B→C。** Script A 式的環境建立(登入 / session / 風扇 / `wait_init` / DUT 清單 / BMC / PCIe 清除 / `pcie_bus` / PMON / dmesg)→ **滿載**`mlucas-avx2 -cpu 0:15` 背景 + `blanton_tr518.sh start`)→ `while 1`PMON + `TH6_SWB0/1_power_readback.sh` + `bgctl list` / `jobs``pause 60`。**沒有收尾段** —— 中斷後 DUT 仍在 `port cd lb=mac` / `l2 learn off` / `test mode nr=yes`,要手動 `blanton_tr518.sh stop` | | **Script 4Thermal / Safety** | `4_Blanton_Script_thermal_safety.ttl` | **獨立腳本,不接 A→B→C。** Script A 式的環境建立(登入 / session / 風扇 / `wait_init` / DUT 清單 / BMC / PCIe 清除 / `pcie_bus` / PMON / dmesg)→ **滿載**`mlucas-avx2 -cpu 0:15` 背景 + `blanton_tr518.sh start`)→ `while 1`PMON + `TH6_SWB0/1_power_readback.sh` + `bgctl list` / `jobs``pause 60`。**沒有收尾段** —— 中斷後 DUT 仍在 `port cd lb=mac` / `l2 learn off` / `test mode nr=yes`,要手動 `blanton_tr518.sh stop` |
@@ -379,6 +382,7 @@ PROFILE_CHANNELS=( "0:high:8" "1:low:8" ... ) # "channel:operation:change_perc
| **100G PRBS** | `port_prbs_monitor.sh {start [both\|a\|b]\|stop\|status\|report\|clear}` | 兩個 uplink 各一個 worker,可同時或單獨跑。setup`lpmode off``prbs set``prbsstat STArt`)→ 週期 `prbs get`(判定)+ `prbsstat Ber`(只記錄)→ teardown`STOp` + `prbs clear`)。輸出含 `PRBS OK!` 才算 PASS。**⚠️ A/B/C 的呼叫目前註解掉** | | **100G PRBS** | `port_prbs_monitor.sh {start [both\|a\|b]\|stop\|status\|report\|clear}` | 兩個 uplink 各一個 worker,可同時或單獨跑。setup`lpmode off``prbs set``prbsstat STArt`)→ 週期 `prbs get`(判定)+ `prbsstat Ber`(只記錄)→ teardown`STOp` + `prbs clear`)。輸出含 `PRBS OK!` 才算 PASS。**⚠️ A/B/C 的呼叫目前註解掉** |
| **SerDes BER 掃描** | `blanton_ber.sh {init\|clear\|start\|stop\|report} [-u 0\|1\|all]` | 全機每個對接 port 的 PRBS BER`prbs set p=3` + `prbs get`(確認每條 lane 都鎖上)→ `prbsstat STArt Interval=30` →(等一個 interval)→ `prbsstat Ber``STOp``prbs clear``report` 一列一條 lane**BER < 1e-6 才 PASS**,最後跨 unit 的 summary 帶最差 lane。⚠️ PRBS 期間 link 顯示 DOWN、不過流量 | | **SerDes BER 掃描** | `blanton_ber.sh {init\|clear\|start\|stop\|report} [-u 0\|1\|all]` | 全機每個對接 port 的 PRBS BER`prbs set p=3` + `prbs get`(確認每條 lane 都鎖上)→ `prbsstat STArt Interval=30` →(等一個 interval)→ `prbsstat Ber``STOp``prbs clear``report` 一列一條 lane**BER < 1e-6 才 PASS**,最後跨 unit 的 summary 帶最差 lane。⚠️ PRBS 期間 link 顯示 DOWN、不過流量 |
| **TR518 負載** | `blanton_tr518.sh {start\|stop} [-u 0\|1\|all]` | `port cd lb=mac``l2 learn off``test mode nr=yes``tr 518 ... testphase=1`,然後 settle `TR_SETTLE_SEC`120s)再讓人量功耗。`stop` 只送 `port cd lb=none`(照 `TR518.txt`),L2 學習與 test mode **不會還原**`TR_RESTORE_*=1` 才會 | | **TR518 負載** | `blanton_tr518.sh {start\|stop} [-u 0\|1\|all]` | `port cd lb=mac``l2 learn off``test mode nr=yes``tr 518 ... testphase=1`,然後 settle `TR_SETTLE_SEC`120s)再讓人量功耗。`stop` 只送 `port cd lb=none`(照 `TR518.txt`),L2 學習與 test mode **不會還原**`TR_RESTORE_*=1` 才會 |
| **PDB / CPB brick** | `pwr_brick.sh show [-r]` | 5 顆 brick 的 Vin / Iin / Vout / Iout / TempSWB0 `0x62`/`0x63`、SWB1 `0x60`/`0x61`、CPB `0x26`)。四顆 ADPM12200 **每個量各自一個 PMBus PAGE**Vin 9、Iin 10、Vout 2、Iout 14、Temp 18),讀之前一定要先寫 PAGE —— `blanton_pwr_data.sh` 完全沒寫,所以整張 PDB 表都是 NA。換算用手冊 DIRECT 公式 `X=(1/m)(Y×10⁻ᴿ−b)`。每個值重讀到通過三道檢查為止(非 `0xFFFF`、非 `0x0000`、落在合理範圍)—— 這條匯流排**只壞高位元組**,`11.98 V` 會變成 `32.00 V`,全一過濾擋不住 |
| **SWB 軌功耗** | `TH6_SWB{0,1}_power_readback.sh` | 讀 `show platform voltage/current` **各一次**,19 條軌 V×I 與總功耗。舊版每軌各讀一次(38 次呼叫、每軌不同時刻),總和是不同瞬間的加總 | | **SWB 軌功耗** | `TH6_SWB{0,1}_power_readback.sh` | 讀 `show platform voltage/current` **各一次**,19 條軌 V×I 與總功耗。舊版每軌各讀一次(38 次呼叫、每軌不同時刻),總和是不同瞬間的加總 |
| **NFC 讀取輪詢** | `nfc_polling.sh {start\|stop\|status\|tail\|report\|clear\|fg}` | 背景重複跑 `blantons_nfc_validate.py`,每輪追加一行機器可讀的 `RESULT=`(PASS/FAIL 取自工具自己的摘要行,兩者皆無則 **ERROR**,與 FAIL 分開計數)。`start` 會先清空 log`report` **只讀 logpolling 中隨時可跑**,並標示 `RUNNING` 免得快照被當最終結果。`tail` **不 follow** | | **NFC 讀取輪詢** | `nfc_polling.sh {start\|stop\|status\|tail\|report\|clear\|fg}` | 背景重複跑 `blantons_nfc_validate.py`,每輪追加一行機器可讀的 `RESULT=`(PASS/FAIL 取自工具自己的摘要行,兩者皆無則 **ERROR**,與 FAIL 分開計數)。`start` 會先清空 log`report` **只讀 logpolling 中隨時可跑**,並標示 `RUNNING` 免得快照被當最終結果。`tail` **不 follow** |
| **USB 目標偵測** | `usb_target.sh [-o dev\|mnt\|both\|id] [-n] [-r sec]` | 找出插入的 USB 儲存裝置,排除 `/``/host` 的底層碟(本平台可能從 USB DOM 開機),多顆時拒絕猜;掛載時會驗證真的可寫 | | **USB 目標偵測** | `usb_target.sh [-o dev\|mnt\|both\|id] [-n] [-r sec]` | 找出插入的 USB 儲存裝置,排除 `/``/host` 的底層碟(本平台可能從 USB DOM 開機),多顆時拒絕猜;掛載時會驗證真的可寫 |
@@ -811,6 +815,15 @@ margin status、全部 VRM 軌的 Vin/Vout/Iout/Temp、CB + ICB 溫感讀值,
- **`--runtime 86400` 是 24 小時的上限,不是「永遠」**:超過一天的 soak,SSD 與 USB 壓力會在 - **`--runtime 86400` 是 24 小時的上限,不是「永遠」**:超過一天的 soak,SSD 與 USB 壓力會在
第 24 小時安靜結束,剩下的時間那兩項是閒置的。Script B 每輪都印 `bgctl list`,數量變少會看得到, 第 24 小時安靜結束,剩下的時間那兩項是閒置的。Script B 每輪都印 `bgctl list`,數量變少會看得到,
但**沒有任何東西會主動報警**。真的要跑更久,得把數字改大或加一段重啟 job 的邏輯。 但**沒有任何東西會主動報警**。真的要跑更久,得把數字改大或加一段重啟 job 的邏輯。
- **`READ_VIN` / `READ_IIN` 不在 ADPM12200 手冊的 Table 3 裡**`pwr_brick.sh` 讓它們沿用
Voltage / Current 那兩組係數。50 V 輸入算得出來、數量級也對,但**手冊沒有明說**。要定案
得找 ADI 確認,或找一個能獨立比對輸入電壓的量測點(`READ_VOUT` 就是靠 `show platform
voltage``PDB_IBC_*` 對上的)。
- **VI2C 沒有硬體號誌**`vi2c_muxsel` 的 help 自己寫了「VI2C has no SEM_REG」),而平台的
sensor 驅動會週期性讀同一批 PDB 裝置。`pwr_brick.sh` 用「重讀到合理為止」繞過去,但根本
問題沒解:`_vrd` 裡的 `_vwait` 逾時後仍然回傳半成品,呼叫端也沒檢查它的回傳值。修
`blanton_icb_vi2c.sh` 會影響所有走 VI2C 的工具(ICB 溫感、風扇、EEPROM),值得先確認
是不是跟 pmon 競爭再動。
- **`blanton_multiphase_margin.sh``margin.sh` 沒有互斥機制**:兩支從不同側(原生 i2c vs - **`blanton_multiphase_margin.sh``margin.sh` 沒有互斥機制**:兩支從不同側(原生 i2c vs
LTC2980 PMBus)摸同一批軌,各自都假設自己是唯一的寫入者。目前只靠文件與 help 提醒, LTC2980 PMBus)摸同一批軌,各自都假設自己是唯一的寫入者。目前只靠文件與 help 提醒,
沒有任何執行期檢查。 沒有任何執行期檢查。
+15 -2
View File
@@ -9,7 +9,7 @@ DUT 端的取數能力由一組 **bash 工具庫** 提供(CB FPGA `pcimem` →
完整架構見 [ARCHITECTURE.md](ARCHITECTURE.md)。 完整架構見 [ARCHITECTURE.md](ARCHITECTURE.md)。
**目前狀態(2026-09-01**Script A 為 **V1.1.5**(A 集中記錄所有 TTL 的變更)。涵蓋 **目前狀態(2026-09-02**Script A 為 **V1.1.6**(A 集中記錄所有 TTL 的變更)。涵蓋
PCIe AER / DDR EDAC / PMON+TPM / 電壓 margin9 顆 LTC2980/ SWB loopback 線速流量 / PCIe AER / DDR EDAC / PMON+TPM / 電壓 margin9 顆 LTC2980/ SWB loopback 線速流量 /
10G+1G 管理網路 ping / BMC DDR 與 USB / NVMe 健康,壓力層走平台的 `bgctl` 10G+1G 管理網路 ping / BMC DDR 與 USB / NVMe 健康,壓力層走平台的 `bgctl`
switch unit 數量由 `config.ttl``SWB_UNIT0` / `SWB_UNIT1` 宣告,風扇轉速由 `FAN_SPEED` 指定 switch unit 數量由 `config.ttl``SWB_UNIT0` / `SWB_UNIT1` 宣告,風扇轉速由 `FAN_SPEED` 指定
@@ -26,7 +26,7 @@ NFC tag 讀取由 `nfc_polling.sh` 背景輪詢(A 做基線、B 起、C 停並
另有 **`4_Blanton_Script_thermal_safety.ttl`**(熱 / 安規,**獨立跑不接 A→B→C**,滿載後每分鐘 另有 **`4_Blanton_Script_thermal_safety.ttl`**(熱 / 安規,**獨立跑不接 A→B→C**,滿載後每分鐘
取 PMON + 兩片 SWB 的軌功耗;**沒有收尾段**,中斷後要手動 `blanton_tr518.sh stop`)。 取 PMON + 兩片 SWB 的軌功耗;**沒有收尾段**,中斷後要手動 `blanton_tr518.sh stop`)。
流程圖:`docs/Blanton_Test_Flow.drawio` 流程圖:`docs/Blanton_Test_Flow.drawio`
最後一次發布是 **V1.1.4**;SWB 的 I2C 通道與位址已於 2026-08-19 上機驗證。 最後一次發布是 **V1.1.5**;SWB 的 I2C 通道與位址已於 2026-08-19 上機驗證。
margin 的 `VNOM` 已於 2026-08-30 改為 Blanton 實測中位數(144 條軌中 86 條上調 0.8~2.5%)。 margin 的 `VNOM` 已於 2026-08-30 改為 Blanton 實測中位數(144 條軌中 86 條上調 0.8~2.5%)。
交付用 `./publish/publish.sh` 打包。 交付用 `./publish/publish.sh` 打包。
@@ -117,6 +117,13 @@ bash ~/Blanton_Script/port_prbs_monitor.sh report # PASS/FAIL 統計(沒
bash ~/Blanton_Script/port_prbs_monitor.sh stop # 收尾會跑 prbsstat STOp + prbs clear bash ~/Blanton_Script/port_prbs_monitor.sh stop # 收尾會跑 prbsstat STOp + prbs clear
# ⚠️ PRBS 打起來時 link 會顯示 down,這是正常的 —— 所以 start 刻意不印 port status # ⚠️ PRBS 打起來時 link 會顯示 down,這是正常的 —— 所以 start 刻意不印 port status
# ── PDB / CPB power brick 讀值 ──────────────────────
./Blanton_Script/pwr_brick.sh show # Vin/Iin/Vout/Iout/Temp × 5 顆
./Blanton_Script/pwr_brick.sh show -r # 多印 raw word
# 四顆 ADPM12200 每個量各一個 PMBus PAGEVin 9 / Iin 10 / Vout 2 / Iout 14 / Temp 18
# CPB.PU37 (0x26) 是不同零件:不寫 PAGE,自己一套換算
# PB_TRIES=1 ... show -r 可看未經重試的原始行為
# ── 多相 VRM margin(原生 i2c,38 條軌)────────────── # ── 多相 VRM margin(原生 i2c,38 條軌)──────────────
./Blanton_Script/blanton_multiphase_margin.sh high3 # 全部 +3% ./Blanton_Script/blanton_multiphase_margin.sh high3 # 全部 +3%
./Blanton_Script/blanton_multiphase_margin.sh low3 # 全部 -3% ./Blanton_Script/blanton_multiphase_margin.sh low3 # 全部 -3%
@@ -232,6 +239,12 @@ Tera Term 連 COM port (115200-8-N-1)
- ⚠️ **ICB/PDB 一律走 VI2C`blanton_icb_vi2c.sh`),不要走 VSPI**VSPI ~18 ms/reg 會讓 I2C - ⚠️ **ICB/PDB 一律走 VI2C`blanton_icb_vi2c.sh`),不要走 VSPI**VSPI ~18 ms/reg 會讓 I2C
byte 相間隔拉到 ~50 ms,踩中 SMBus 2535 ms timeout,裝置直接 NACK。實測全掃 9.4 s vs 34.0 s。 byte 相間隔拉到 ~50 ms,踩中 SMBus 2535 ms timeout,裝置直接 NACK。實測全掃 9.4 s vs 34.0 s。
- ⚠️ **I2C 讀一律 Repeated START**(不是 STOP + START),否則 PMBus/SMBus 裝置(VRM、EFUSE)不理。 - ⚠️ **I2C 讀一律 Repeated START**(不是 STOP + START),否則 PMBus/SMBus 裝置(VRM、EFUSE)不理。
- ⚠️ **VI2C 這條路的壞法是「只壞高位元組」**,不是整包壞掉。`11.98 V` 會變成 `32.00 V`
—— 正的、數量級也不誇張,**擋全一/全零的過濾完全攔不到**。寫任何 VI2C 讀值工具都要有
合理範圍檢查 + 重讀,光靠 `PDB_REJECT_FFFF` 那種是不夠的(`pwr_brick.sh` 的教訓)
- ⚠️ **PMBus 的 PAGE 不是只有 VRM 通道在用**:ADPM12200 把每個量測放在不同 PAGE
Vin 9 / Iin 10 / Vout 2 / Iout 14 / Temp 18),沒寫 PAGE 就讀,回的是 `0xFFFF`
`0x0000``blanton_pwr_data.sh` 的 PDB 表 page 是 `-`,這就是它整張表 NA 的原因
- ⚠️ **`--runtime 86400` 是 24 小時上限**:超過一天的 soak,SSD / USB 壓力會安靜結束, - ⚠️ **`--runtime 86400` 是 24 小時上限**:超過一天的 soak,SSD / USB 壓力會安靜結束,
剩下的時間那兩項閒置。`bgctl list` 的數量變少是唯一的線索,**不會報警** 剩下的時間那兩項閒置。`bgctl list` 的數量變少是唯一的線索,**不會報警**
- ⚠️ **多相 VRM 與 LTC2980 是同一批軌的兩條路**`blanton_multiphase_margin.sh`(原生 i2c - ⚠️ **多相 VRM 與 LTC2980 是同一批軌的兩條路**`blanton_multiphase_margin.sh`(原生 i2c
Binary file not shown.
+69
View File
@@ -0,0 +1,69 @@
# v1.1.6 — The PDB bricks were never being asked the right question
## 🐛 Bug fixes
**Every PDB reading was `NA`, and the cause was a page number written in the wrong base**
* The four ADPM12200 bricks put each measurement on its own PMBus PAGE, and the page has to be written before every read. The bring-up spreadsheet listed those page numbers in decimal but with an `0x` prefix, so four of the five selected the wrong page:
| Reading | Datasheet PAGE | Should send | Spreadsheet said |
|---|---|---|---|
| Vin | 9 | `0x09` | `0x00` |
| Iin | 10 | `0x0A` | `0x10` |
| Vout | 2 | `0x02` | `0x02` ✅ |
| Iout | 14 | `0x0E` | `0x14` |
| Temp | 18 | `0x12` | `0x18` |
* Only Vout worked — because 2 reads the same in either base. That is why Iin and Iout answered `0xFFFF` and the temperature answered `0x0000`, and why a Vin scaling had to be invented to make 50 V appear.
* `blanton_pwr_data.sh` sends no page at all (its PDB table has `page "-"`), which is the reason it reports the whole PDB section as `NA`.
## ✨ New features
**`pwr_brick.sh` — the five power bricks, read properly**
* `show` prints Vin / Iin / Vout / Iout / Temp for all five: `SWB0.PU207` (0x62), `SWB0.PU13` (0x63), `SWB1.PU332` (0x60), `SWB1.PU85` (0x61) and `CPB.PU37` (0x26). `-r` adds the raw words.
* Conversions come from the datasheet's DIRECT equation, `X = (1/m)(Y × 10⁻ᴿ b)`, with Table 3's coefficients: voltage `Y × 8 mV`, current `Y × 0.04 A`, temperature `Y × 0.01 °C`.
* **Cross-checked, not assumed**: `READ_VOUT` returns `0x05D4` → 1492 × 8 = 11936 mV, which is exactly the figure `show platform voltage` reports for that rail from a completely separate sensor path.
* `CPB.PU37` is a different part — no page writes and its own scaling — and is handled separately.
* The datasheet is now in the repo (`docs/ADPM12200CMLZxx_DS_*.pdf`) so the pages and coefficients can be checked without hunting for it.
**Every value is validated before it is printed**
* This bus corrupts the **high byte only**. That turns `11.98 V` into `32.00 V` — positive, plausible in magnitude, and invisible to any "reject 0xFFFF" filter. Each value is re-read (up to `PB_TRIES`, default 8) until it is not `0xFFFF`, not `0x0000`, and lands inside a plausibility window.
* `0x0000` is rejected as a raw word for the same reason: on the temperature register it decodes to a perfectly believable 0 °C.
**Complementary margin profiles per board**
* `comboA` / `comboB` for CB and each SWB CONN. `comboB` is `comboA` with every direction flipped, so running both covers each rail high and low while its neighbours sit the other way.
* NC channels stay at `nominal` in both — there is nothing to margin on an unused rail (CONN14 CH10/11/13, CONN15 CH10).
## 📦 Downloads
| File | Contents |
|---|---|
| `Script_ABC_Blanton_V1.1.6.zip` | The full Tera Term working directory, including `Blanton_Script/` to copy onto the DUT |
**After copying to the DUT:**
```bash
chmod +x ~/Blanton_Script/*.sh # see below
grep -rlU $'\r' ~/Blanton_Script # expect no output
```
`blanton_ber.sh`, `nfc_polling.sh`, `blanton_tr518.sh`, `blanton_multiphase_margin.sh`, `pwr_brick.sh` and the two `TH6_SWB*_power_readback.sh` are invoked through `./` or `~/`, so they need the execute bit along with `bmc_monitor.sh` and `usb_target.sh` — neither git nor a Windows/USB copy carries it reliably.
## ⚠️ Before you run
* **`READ_VIN` and `READ_IIN` are not in the datasheet's coefficient table.** They are decoded here with the voltage and current coefficients — consistent with a ~50 V input, but not stated by the datasheet. Anything quoting brick Vin should have that confirmed first. `PB_VIN_M` / `PB_IIN_M` are where to change it.
* **`NA` has two meanings**: nothing readable, or read but outside its plausibility window. Use `PB_TRIES=1 ./Blanton_Script/pwr_brick.sh show -r` to see the unretried raw words and tell them apart.
* **Margin profiles are positional.** `comboA_SWB_CONN13` applied to a CONN16 board margins the wrong rails and nothing detects it.
* **`blanton_multiphase_margin.sh save` is permanent**, and it must not be mixed with `margin.sh` — same rails, two access paths, no interlock.
* **A soak longer than 24 hours needs the stress runtimes raised** — `--runtime 86400` is a ceiling.
* **Script 4 (thermal/safety) has no teardown** — finish with `blanton_tr518.sh stop` and `kill $(jobs -p)`.
* **These three cannot share the switch**: `blanton_ber.sh`, `blanton_tr518.sh`, `blanton_traffic_linespeed`.
* **`FAN_SPEED` is 100.**
* **The three items listed as unverified in v1.1.0 are still unverified**: the `ce0` ingress mirror, the 120s settle in `blanton_tr518.sh`, and `test mode nr=no` on stop.
## 🔗 Links
* [ARCHITECTURE.md](https://etgit.et-wen.com/etwen/Blanton_TTL_Script/src/branch/main/ARCHITECTURE.md) — test flow, data models, constraints
* [CLAUDE.md](https://etgit.et-wen.com/etwen/Blanton_TTL_Script/src/branch/main/CLAUDE.md) — commands and the bench gotchas
* [Blanton_Test_Flow.drawio](https://etgit.et-wen.com/etwen/Blanton_TTL_Script/src/branch/main/docs/Blanton_Test_Flow.drawio) — the four scripts side by side
**Full changelog:** [V1.1.5...V1.1.6](https://etgit.et-wen.com/etwen/Blanton_TTL_Script/compare/V1.1.5...V1.1.6)
+39 -2
View File
@@ -1,7 +1,7 @@
; ============================================================================= ; =============================================================================
; Script A for Blanton ; Script A for Blanton
; Version : V1.1.5 ; Version : V1.1.6
; Date : 2026-09-01 ; Date : 2026-09-02
; Author : ETWen ; Author : ETWen
; ============================================================================= ; =============================================================================
; Version History: ; Version History:
@@ -233,6 +233,43 @@
; counter totals are not comparable across this ; counter totals are not comparable across this
; build - the PASS/FAIL cross-check is unaffected ; build - the PASS/FAIL cross-check is unaffected
; tools/Multiphase_*.txt Bench command references for the new script ; tools/Multiphase_*.txt Bench command references for the new script
; V1.1.6 2026-09-02 Blanton_Script/pwr_brick.sh Add PDB / CPB power brick readings
; 5 devices, Vin / Iin / Vout / Iout / Temp:
; SWB0.PU207 0x62 SWB0.PU13 0x63
; SWB1.PU332 0x60 SWB1.PU85 0x61
; CPB.PU37 0x26
; The four ADPM12200 put each measurement on its own
; PMBus PAGE, so a PAGE write has to precede every
; read - blanton_pwr_data.sh sends none, which is why
; it reports the whole PDB table as NA
; Vin PAGE 9 Iin PAGE 10 Vout PAGE 2
; Iout PAGE 14 Temp PAGE 18
; !! the bring-up spreadsheet wrote those page numbers
; in decimal but with an 0x prefix (10 -> 0x10,
; 14 -> 0x14, 18 -> 0x18). Only Vout worked,
; because 2 reads the same either way
; Conversions are the datasheet DIRECT equation
; X = (1/m)(Y x 10^-R - b), Table 3: voltage Y x 8mV,
; current Y x 0.04A, temperature Y x 0.01C
; Checked: READ_VOUT 0x05D4 -> 11936 mV, the same
; figure show platform reports for that rail
; !! Table 3 does NOT list READ_VIN / READ_IIN; they
; borrow the voltage / current coefficients here
; Each value is re-read until it passes: not 0xFFFF,
; not 0x0000, and inside a plausibility window. This
; bus corrupts the HIGH BYTE only, which turns 11.98 V
; into 32.00 V - a value no all-ones filter catches
; docs/ADPM12200..._Datasheet_2025_12_19.pdf The brick datasheet the pages and coefficients above
; came from
; ScriptC Re-enable the BMC USB journalctl dump - it had
; been commented out, so the service log never
; reached the master log
; LTC2980_Margin_Script/profiles/ Add comboA / comboB per board (CB + SWB CONN13..16)
; Checkerboard pair: comboB is comboA with every
; direction flipped, so running both covers each rail
; high and low while its neighbours sit the other way
; NC channels stay nominal in both (CONN14 CH10/11/13,
; CONN15 CH10) - nothing to margin on an unused rail
; ============================================================================= ; =============================================================================
include "config.ttl" include "config.ttl"
@@ -78,8 +78,8 @@ wait prompt_sonic_root
sendln "cat ./Blanton_Script/log/bmc_poll.log" sendln "cat ./Blanton_Script/log/bmc_poll.log"
; ========== BMC USB Test result ========== ; ========== BMC USB Test result ==========
;wait prompt_sonic_root wait prompt_sonic_root
;sendln "journalctl -u qfx5252-bmc-usb-net-test.service --no-pager" sendln "journalctl -u qfx5252-bmc-usb-net-test.service --no-pager"
; ========== CHECK Stress results ========== ; ========== CHECK Stress results ==========
@@ -0,0 +1,26 @@
# Profile: comboA_CB - checkerboard, the complement of comboB_CB
# Board: CB only. The channel list is positional, so applying this to a
# different board margins the wrong rails and nothing detects it.
# Run comboA then comboB to cover every rail in both directions while its
# neighbours sit the other way.
# Applied to whichever LTC2980 is currently loaded via margin_init
# Format: "channel:operation:change_percent"
PROFILE_CHANNELS=(
"0:low:5"
"1:low:5"
"2:high:5"
"3:low:5"
"4:high:5"
"5:high:5"
"6:high:5"
"7:high:5"
"8:low:5"
"9:high:5"
"10:high:5"
"11:high:5"
"12:low:5"
"13:high:5"
"14:high:5"
"15:low:5"
)
@@ -0,0 +1,26 @@
# Profile: comboA_SWB_CONN13 - checkerboard, the complement of comboB_SWB_CONN13
# Board: SWB_CONN13 only. The channel list is positional, so applying this to a
# different board margins the wrong rails and nothing detects it.
# Run comboA then comboB to cover every rail in both directions while its
# neighbours sit the other way.
# Applied to whichever LTC2980 is currently loaded via margin_init
# Format: "channel:operation:change_percent"
PROFILE_CHANNELS=(
"0:high:3"
"1:high:3"
"2:low:3"
"3:low:3"
"4:high:5"
"5:low:5"
"6:high:5"
"7:high:5"
"8:high:5"
"9:high:3"
"10:high:3"
"11:low:3"
"12:low:3"
"13:high:3"
"14:high:3"
"15:low:5"
)
@@ -0,0 +1,28 @@
# Profile: comboA_SWB_CONN14 - checkerboard, the complement of comboB_SWB_CONN14
# Board: SWB_CONN14 only. The channel list is positional, so applying this to a
# different board margins the wrong rails and nothing detects it.
# Run comboA then comboB to cover every rail in both directions while its
# neighbours sit the other way.
# CH 10, 11, 13 stay nominal: they are NC on this board, so there is
# nothing to margin.
# Applied to whichever LTC2980 is currently loaded via margin_init
# Format: "channel:operation:change_percent"
PROFILE_CHANNELS=(
"0:high:3"
"1:low:3"
"2:low:3"
"3:high:5"
"4:high:5"
"5:high:5"
"6:low:5"
"7:low:5"
"8:low:3"
"9:low:3"
"10:nominal:5"
"11:nominal:5"
"12:high:3"
"13:nominal:5"
"14:high:5"
"15:high:3"
)
@@ -0,0 +1,28 @@
# Profile: comboA_SWB_CONN15 - checkerboard, the complement of comboB_SWB_CONN15
# Board: SWB_CONN15 only. The channel list is positional, so applying this to a
# different board margins the wrong rails and nothing detects it.
# Run comboA then comboB to cover every rail in both directions while its
# neighbours sit the other way.
# CH 10 stay nominal: they are NC on this board, so there is
# nothing to margin.
# Applied to whichever LTC2980 is currently loaded via margin_init
# Format: "channel:operation:change_percent"
PROFILE_CHANNELS=(
"0:low:3"
"1:low:3"
"2:high:3"
"3:high:3"
"4:low:3"
"5:low:3"
"6:high:5"
"7:high:3"
"8:high:5"
"9:low:5"
"10:nominal:5"
"11:low:5"
"12:low:3"
"13:high:3"
"14:high:3"
"15:high:5"
)
@@ -0,0 +1,26 @@
# Profile: comboA_SWB_CONN16 - checkerboard, the complement of comboB_SWB_CONN16
# Board: SWB_CONN16 only. The channel list is positional, so applying this to a
# different board margins the wrong rails and nothing detects it.
# Run comboA then comboB to cover every rail in both directions while its
# neighbours sit the other way.
# Applied to whichever LTC2980 is currently loaded via margin_init
# Format: "channel:operation:change_percent"
PROFILE_CHANNELS=(
"0:high:3"
"1:high:5"
"2:high:5"
"3:low:5"
"4:high:5"
"5:low:5"
"6:high:5"
"7:high:3"
"8:high:3"
"9:high:3"
"10:low:3"
"11:low:3"
"12:low:3"
"13:low:3"
"14:low:3"
"15:high:3"
)
@@ -0,0 +1,26 @@
# Profile: comboB_CB - checkerboard, the complement of comboA_CB
# Board: CB only. The channel list is positional, so applying this to a
# different board margins the wrong rails and nothing detects it.
# Run comboB then comboA to cover every rail in both directions while its
# neighbours sit the other way.
# Applied to whichever LTC2980 is currently loaded via margin_init
# Format: "channel:operation:change_percent"
PROFILE_CHANNELS=(
"0:high:5"
"1:high:5"
"2:low:5"
"3:high:5"
"4:low:5"
"5:low:5"
"6:low:5"
"7:low:5"
"8:high:5"
"9:low:5"
"10:low:5"
"11:low:5"
"12:high:5"
"13:low:5"
"14:low:5"
"15:high:5"
)
@@ -0,0 +1,26 @@
# Profile: comboB_SWB_CONN13 - checkerboard, the complement of comboA_SWB_CONN13
# Board: SWB_CONN13 only. The channel list is positional, so applying this to a
# different board margins the wrong rails and nothing detects it.
# Run comboB then comboA to cover every rail in both directions while its
# neighbours sit the other way.
# Applied to whichever LTC2980 is currently loaded via margin_init
# Format: "channel:operation:change_percent"
PROFILE_CHANNELS=(
"0:low:3"
"1:low:3"
"2:high:3"
"3:high:3"
"4:low:5"
"5:high:5"
"6:low:5"
"7:low:5"
"8:low:5"
"9:low:3"
"10:low:3"
"11:high:3"
"12:high:3"
"13:low:3"
"14:low:3"
"15:high:5"
)
@@ -0,0 +1,28 @@
# Profile: comboB_SWB_CONN14 - checkerboard, the complement of comboA_SWB_CONN14
# Board: SWB_CONN14 only. The channel list is positional, so applying this to a
# different board margins the wrong rails and nothing detects it.
# Run comboB then comboA to cover every rail in both directions while its
# neighbours sit the other way.
# CH 10, 11, 13 stay nominal: they are NC on this board, so there is
# nothing to margin.
# Applied to whichever LTC2980 is currently loaded via margin_init
# Format: "channel:operation:change_percent"
PROFILE_CHANNELS=(
"0:low:3"
"1:high:3"
"2:high:3"
"3:low:5"
"4:low:5"
"5:low:5"
"6:high:5"
"7:high:5"
"8:high:3"
"9:high:3"
"10:nominal:5"
"11:nominal:5"
"12:low:3"
"13:nominal:5"
"14:low:5"
"15:low:3"
)
@@ -0,0 +1,28 @@
# Profile: comboB_SWB_CONN15 - checkerboard, the complement of comboA_SWB_CONN15
# Board: SWB_CONN15 only. The channel list is positional, so applying this to a
# different board margins the wrong rails and nothing detects it.
# Run comboB then comboA to cover every rail in both directions while its
# neighbours sit the other way.
# CH 10 stay nominal: they are NC on this board, so there is
# nothing to margin.
# Applied to whichever LTC2980 is currently loaded via margin_init
# Format: "channel:operation:change_percent"
PROFILE_CHANNELS=(
"0:high:3"
"1:high:3"
"2:low:3"
"3:low:3"
"4:high:3"
"5:high:3"
"6:low:5"
"7:low:3"
"8:low:5"
"9:high:5"
"10:nominal:5"
"11:high:5"
"12:high:3"
"13:low:3"
"14:low:3"
"15:low:5"
)
@@ -0,0 +1,26 @@
# Profile: comboB_SWB_CONN16 - checkerboard, the complement of comboA_SWB_CONN16
# Board: SWB_CONN16 only. The channel list is positional, so applying this to a
# different board margins the wrong rails and nothing detects it.
# Run comboB then comboA to cover every rail in both directions while its
# neighbours sit the other way.
# Applied to whichever LTC2980 is currently loaded via margin_init
# Format: "channel:operation:change_percent"
PROFILE_CHANNELS=(
"0:low:3"
"1:low:5"
"2:low:5"
"3:high:5"
"4:low:5"
"5:high:5"
"6:low:5"
"7:low:3"
"8:low:3"
"9:low:3"
"10:high:3"
"11:high:3"
"12:high:3"
"13:high:3"
"14:high:3"
"15:low:3"
)
@@ -0,0 +1,241 @@
#!/usr/bin/env bash
# =============================================================================
# pwr_brick.sh -- PDB / CPB power brick Vin / Iin / Vout / Iout / Temp
# =============================================================================
# Version History:
# V1.1.0 2026-09-02 PAGE values and conversions taken from the ADPM12200
# datasheet instead of the bring-up spreadsheet.
# The spreadsheet wrote every PAGE in decimal but with an
# 0x prefix, so four of the five selected the wrong page:
# Vin PAGE 9 -> it said 0x00 (should be 0x09)
# Iin PAGE 10 -> it said 0x10 (should be 0x0A)
# Vout PAGE 2 -> it said 0x02 correct by luck
# Iout PAGE 14 -> it said 0x14 (should be 0x0E)
# Temp PAGE 18 -> it said 0x18 (should be 0x12)
# Only Vout worked, because 2 reads the same either way.
# That is why Iin/Iout answered 0xFFFF and Temp 0x0000.
# Conversions now use the datasheet DIRECT equation; the
# reverse-engineered BRICK_VIN_LSB is gone.
# V1.0.0 2026-09-02 First version.
# =============================================================================
#
# Path: CB FPGA F3 VI2C ch4 -> TCA954x mux 0x72 ch0 -> the bricks
#
# The four ADPM12200 modules put each measurement on its own PMBus PAGE, so a
# PAGE write has to precede every read. blanton_pwr_data.sh does not send one
# at all (its PDB table has page "-"), which is why it reports them as NA.
#
# READ_VIN 0x88 PAGE 9
# READ_IIN 0x89 PAGE 10
# READ_VOUT 0x8B PAGE 2
# READ_IOUT 0x8C PAGE 14
# READ_TEMPERATURE_1 0x8D PAGE 18
#
# DIRECT format, datasheet Table 3 ("PMBus Command Code Coefficients"):
#
# X = (1/m) x (Y x 10^-R - b) Y = the 2-byte two's complement word
#
# Voltage m=0.125 b=0 R=0 units mV -> X = Y x 8 mV
# Current m=0.25 b=0 R=2 units A -> X = Y x 0.04 A
# Temperature m=1 b=0 R=2 units degC -> X = Y x 0.01 degC
#
# Sanity check: READ_VOUT 0x05D4 = 1492 -> 1492 x 8 = 11936 mV, which is exactly
# what PDB_IBC_SWB1_A_VOUT reports in 'show platform voltage'.
#
# ⚠️ Table 3 does not list READ_VIN or READ_IIN. They are decoded here with the
# Voltage and Current coefficients respectively - reasonable, and consistent
# with a ~50 V input, but not stated by the datasheet. PB_VIN_M / PB_IIN_M
# below are where to change that if the part turns out to differ.
#
# ⚠️ CPB.PU37 (0x26) is a DIFFERENT part: no PAGE writes, and its own scaling
# (Vin 0.25 V, Vout 2^-11 V, Iout 0.5 A per count of the mantissa). Those
# three were confirmed against words this DUT returns.
#
# Usage:
# ./pwr_brick.sh show # the table
# ./pwr_brick.sh show -r # add the raw words
# ./pwr_brick.sh help
# =============================================================================
# --- User Configurable Section ----------------------------------------------
PB_CH="${PB_CH:-4}" # ICB VI2C channel
PB_MUX="${PB_MUX:-0x72}" # TCA954x address
PB_MUX_SEL="${PB_MUX_SEL:-0x01}" # control byte: bit0 = channel 0
PB_TRIES="${PB_TRIES:-8}" # reads per value before giving up
# PMBus PAGE per measurement (ADPM12200). Decimal page numbers written in hex.
PB_PAGE_VIN="${PB_PAGE_VIN:-0x09}" # 9
PB_PAGE_IIN="${PB_PAGE_IIN:-0x0A}" # 10
PB_PAGE_VOUT="${PB_PAGE_VOUT:-0x02}" # 2
PB_PAGE_IOUT="${PB_PAGE_IOUT:-0x0E}" # 14
PB_PAGE_TEMP="${PB_PAGE_TEMP:-0x12}" # 18
# DIRECT coefficients: m, R, and the divisor that takes the datasheet unit to
# the displayed unit (mV -> V is 1000; A and degC are already right).
PB_VOUT_M="${PB_VOUT_M:-0.125}"; PB_VOUT_R="${PB_VOUT_R:-0}"; PB_VOUT_DIV="${PB_VOUT_DIV:-1000}"
PB_VIN_M="${PB_VIN_M:-0.125}"; PB_VIN_R="${PB_VIN_R:-0}"; PB_VIN_DIV="${PB_VIN_DIV:-1000}"
PB_IOUT_M="${PB_IOUT_M:-0.25}"; PB_IOUT_R="${PB_IOUT_R:-2}"; PB_IOUT_DIV="${PB_IOUT_DIV:-1}"
PB_IIN_M="${PB_IIN_M:-0.25}"; PB_IIN_R="${PB_IIN_R:-2}"; PB_IIN_DIV="${PB_IIN_DIV:-1}"
PB_TEMP_M="${PB_TEMP_M:-1}"; PB_TEMP_R="${PB_TEMP_R:-2}"; PB_TEMP_DIV="${PB_TEMP_DIV:-1}"
# CPB.PU37 only (different part, no PAGE, mantissa-based)
CPB_VIN_LSB="${CPB_VIN_LSB:-0.25}" # V per count of the mantissa
CPB_IOUT_LSB="${CPB_IOUT_LSB:-0.5}" # A per count of the mantissa
# Plausibility windows. A decoded value outside its window is discarded and the
# read retried; this is what rejects the corrupted words this bus produces - a
# bad high byte turns 11.98 V into 32.00 V, which "reject 0xFFFF" never catches.
PB_VIN_LO="${PB_VIN_LO:-30}"; PB_VIN_HI="${PB_VIN_HI:-70}"
PB_VOUT_LO="${PB_VOUT_LO:-8}"; PB_VOUT_HI="${PB_VOUT_HI:-14}"
PB_IIN_LO="${PB_IIN_LO:-0}"; PB_IIN_HI="${PB_IIN_HI:-100}"
PB_IOUT_LO="${PB_IOUT_LO:-0}"; PB_IOUT_HI="${PB_IOUT_HI:-300}"
PB_TEMP_LO="${PB_TEMP_LO:-0}"; PB_TEMP_HI="${PB_TEMP_HI:-125}"
# All-ones and all-zeros are the two ways this bus says "nobody answered", and
# both can decode into a value that sits inside its window - 0x0000 on the
# temperature register reads as a perfectly plausible 0 C. Rejected as raw
# words, before any conversion, so no window has to be bent to catch them.
PB_REJECT_FFFF="${PB_REJECT_FFFF:-1}"
PB_REJECT_0000="${PB_REJECT_0000:-1}"
# "location:bricks:slave:netname:kind" kind = adpm (PAGE per read) | cpb
PB_DEVS=(
"SWB0.PU207:BRICKS1:0x62:P12V_SWB0_L:adpm"
"SWB0.PU13:BRICKS2:0x63:P12V_SWB0_L:adpm"
"SWB1.PU332:BRICKS1:0x60:P12V_SWB1_R:adpm"
"SWB1.PU85:BRICKS2:0x61:P12V_SWB1_R:adpm"
"CPB.PU37:BRICK:0x26:P12V_STBY:cpb"
)
# -----------------------------------------------------------------------------
PB_NA="NA"
PB_RAW=0
PB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" 2>/dev/null && pwd)"
_pb_err() { echo -e "[\033[31mERR \033[0m] $*" >&2; }
# --- backend -----------------------------------------------------------------
if ! declare -F vi2c_read >/dev/null 2>&1; then
if [ -r "$PB_DIR/blanton_icb_vi2c.sh" ]; then
# shellcheck source=/dev/null
source "$PB_DIR/blanton_icb_vi2c.sh" >/dev/null 2>&1
else
_pb_err "blanton_icb_vi2c.sh not found next to this script"; exit 1
fi
fi
declare -F vi2c_read >/dev/null 2>&1 || { _pb_err "vi2c_read() still undefined"; exit 1; }
# --- decoders ----------------------------------------------------------------
# DIRECT: X = (1/m) x (Y x 10^-R - b), b = 0. Y is two's complement, so a word
# above 0x7FFF is negative - that matters for temperature, not for the rails.
_pb_direct() {
awk -v w=$(( $1 )) -v m="$2" -v r="$3" -v d="$4" 'BEGIN{
if (w > 32767) w -= 65536
printf "%.3f", ((w * (10 ^ -r)) / m) / d
}'
}
# SLINEAR11 mantissa only (bits[10:0], two's complement) - CPB.PU37 path.
_pb_mant() { awk -v w=$(( $1 )) 'BEGIN{ m = w % 2048; if (m > 1023) m -= 2048; print m }'; }
_pb_mul() { awk -v a="$1" -v b="$2" 'BEGIN{ printf "%.3f", a * b }'; }
_pb_lin11() { awk -v w=$(( $1 )) 'BEGIN{ printf "%.4f", w * (2.0 ^ -11) }'; }
_pb_in() { awk -v x="$1" -v lo="$2" -v hi="$3" 'BEGIN{ exit !(x >= lo && x <= hi) }'; }
# _pb_word <slave> <page|-> <reg> -> "0xWWWW"
_pb_word() {
local slave="$1" page="$2" reg="$3" out
[ "$page" != "-" ] && vi2c_write "$PB_CH" "$slave" 0x00 "$page" >/dev/null 2>&1
out=$(vi2c_read "$PB_CH" "$slave" "$reg" 2 2>/dev/null \
| sed -n 's/.*\[0x\(..\) 0x\(..\)\].*/0x\2\1/p')
[ -n "$out" ] && echo "$out"
}
# _pb_val <slave> <page|-> <reg> <decoder> <a1> <a2> <a3> <lo> <hi>
# Retries until the decoded value lands inside the window; echoes "value raw".
_pb_val() {
local slave="$1" page="$2" reg="$3" dec="$4" a1="$5" a2="$6" a3="$7" lo="$8" hi="$9"
local i w v
for (( i = 0; i < PB_TRIES; i++ )); do
w=$(_pb_word "$slave" "$page" "$reg") || continue
[ -z "$w" ] && continue
[ "$PB_REJECT_FFFF" = "1" ] && [ "$(( w ))" -eq 65535 ] && continue
[ "$PB_REJECT_0000" = "1" ] && [ "$(( w ))" -eq 0 ] && continue
case "$dec" in
direct) v=$(_pb_direct "$w" "$a1" "$a2" "$a3") ;;
mant) v=$(_pb_mul "$(_pb_mant "$w")" "$a1") ;;
lin11) v=$(_pb_lin11 "$w") ;;
esac
_pb_in "$v" "$lo" "$hi" || continue
echo "$v $w"; return 0
done
echo "$PB_NA -"; return 1
}
_pb_mux_open() {
# The mux takes a bare control byte and vi2c_write always sends a register
# first, so passing 0x01 as both makes the part latch 0x01 either way.
vi2c_init "$PB_CH" >/dev/null 2>&1
vi2c_write "$PB_CH" "$PB_MUX" "$PB_MUX_SEL" "$PB_MUX_SEL" >/dev/null 2>&1
return 0
}
# --- show --------------------------------------------------------------------
pwr_brick_show() {
_pb_mux_open
echo "==== PDB / CPB power bricks (VI2C ch${PB_CH} -> mux ${PB_MUX} ch0) ===="
printf '%-3s %-12s %-8s %-7s %-14s %10s %10s %10s %10s %9s\n' \
"No." "Location" "Bricks" "SlaveID" "Netname" \
"Vin(V)" "Iin(A)" "Vout(V)" "Iout(A)" "Temp(C)"
printf '%s\n' "--------------------------------------------------------------------------------------------------------------"
local n=0 entry loc bk slave net kind vi ii vo io tp
for entry in "${PB_DEVS[@]}"; do
IFS=':' read -r loc bk slave net kind <<< "$entry"
n=$(( n + 1 ))
if [ "$kind" = "cpb" ]; then
vi=$(_pb_val "$slave" - 0x88 mant "$CPB_VIN_LSB" "" "" "$PB_VIN_LO" "$PB_VIN_HI")
ii="$PB_NA -" # no input current on this part
vo=$(_pb_val "$slave" - 0x8B lin11 "" "" "" "$PB_VOUT_LO" "$PB_VOUT_HI")
io=$(_pb_val "$slave" - 0x8C mant "$CPB_IOUT_LSB" "" "" "$PB_IOUT_LO" "$PB_IOUT_HI")
tp=$(_pb_val "$slave" - 0x8D direct 1 2 1 "$PB_TEMP_LO" "$PB_TEMP_HI")
else
vi=$(_pb_val "$slave" "$PB_PAGE_VIN" 0x88 direct "$PB_VIN_M" "$PB_VIN_R" "$PB_VIN_DIV" "$PB_VIN_LO" "$PB_VIN_HI")
ii=$(_pb_val "$slave" "$PB_PAGE_IIN" 0x89 direct "$PB_IIN_M" "$PB_IIN_R" "$PB_IIN_DIV" "$PB_IIN_LO" "$PB_IIN_HI")
vo=$(_pb_val "$slave" "$PB_PAGE_VOUT" 0x8B direct "$PB_VOUT_M" "$PB_VOUT_R" "$PB_VOUT_DIV" "$PB_VOUT_LO" "$PB_VOUT_HI")
io=$(_pb_val "$slave" "$PB_PAGE_IOUT" 0x8C direct "$PB_IOUT_M" "$PB_IOUT_R" "$PB_IOUT_DIV" "$PB_IOUT_LO" "$PB_IOUT_HI")
tp=$(_pb_val "$slave" "$PB_PAGE_TEMP" 0x8D direct "$PB_TEMP_M" "$PB_TEMP_R" "$PB_TEMP_DIV" "$PB_TEMP_LO" "$PB_TEMP_HI")
fi
printf '%-3s %-12s %-8s %-7s %-14s %10s %10s %10s %10s %9s\n' \
"$n" "$loc" "$bk" "$slave" "$net" \
"${vi%% *}" "${ii%% *}" "${vo%% *}" "${io%% *}" "${tp%% *}"
[ "$PB_RAW" = "1" ] && printf '%-3s %-12s %-8s %-7s %-14s %10s %10s %10s %10s %9s\n' \
"" "" "" "" "raw" "${vi##* }" "${ii##* }" "${vo##* }" "${io##* }" "${tp##* }"
done
# No trailing notes on stdout: this table gets pasted into reports and
# captured into the master console log, and a caveat printed every run stops
# being read after the second time. The caveats live in the file header -
# the one that still matters is that READ_VIN / READ_IIN are not in the
# datasheet coefficient table and borrow the Voltage / Current sets.
}
pwr_brick_help() {
echo -e "\033[1mpwr_brick.sh - PDB / CPB power brick readings\033[0m"
echo ""
echo -e " \033[33mshow\033[0m [-r] Vin / Iin / Vout / Iout / Temp for all 5 bricks (-r adds raw)"
echo -e " \033[33mhelp\033[0m"
echo ""
echo -e " Path : VI2C ch$PB_CH -> mux $PB_MUX ch0"
echo -e " The four ADPM12200 modules need a PMBus PAGE per measurement:"
echo -e " Vin $PB_PAGE_VIN(9) Iin $PB_PAGE_IIN(10) Vout $PB_PAGE_VOUT(2) Iout $PB_PAGE_IOUT(14) Temp $PB_PAGE_TEMP(18)"
echo -e " DIRECT format, datasheet Table 3: X = (1/m) x (Y x 10^-R - b)"
echo -e " CPB.PU37 (0x26) is a different part - no PAGE, its own scaling."
echo -e " \033[33mREAD_VIN / READ_IIN are not in Table 3; they borrow Voltage / Current.\033[0m"
}
case "${1:-help}" in
show)
shift 2>/dev/null
[ "${1:-}" = "-r" ] && PB_RAW=1
pwr_brick_show ;;
help|-h|--help) pwr_brick_help ;;
*) _pb_err "unknown sub-command: $1"; pwr_brick_help; exit 1 ;;
esac