# ArmLite MCU 開發與測試簡易指南

## 1. 文件目的

本文件說明 IT9860 SDK 中 ArmLite MCU 測試專案的架構、設定、建置、執行與除錯方式，主要對應以下目錄：

- `project/test_armliteMcu`：Host CPU 測試程式、Kconfig 與測試判定。
- `sdk/arm_lite_dev/armliteMcu`：ArmLite MCU firmware 與各周邊模組。
- `tool/bin/PrintArmLiteMcuDump`：讀取 ArmLite MCU memory log 與 exception backtrace 的工具。

## 2. 系統架構

Host CPU 啟動 OpenRTOS 後，由 `test_openrtos.c` 建立測試 thread，進入 `test_armlite_mcu.c` 的 `TestFunc()`。Host 先設定 ArmLite 專用 GPIO，再切換 ArmLite engine、載入 firmware，並透過 shared memory telemetry 監看執行狀態。

ArmLite MCU 從 `sdk/arm_lite_dev/armliteMcu/main.c` 啟動，初始化啟用的周邊模組，並以 1 ms tick 驅動 scheduler。GPIO 與 SARADC task 預設每 10 ms 執行；UART、frequency 與 interrupt test 使用 polling 或 interrupt 處理。

Telemetry 將 heartbeat、各測試 counter、最後測量值與 error count 發佈到 shared memory1。Host 預設每秒取得 snapshot，連續三次健康後顯示 PASS；已經 PASS 的測試如果之後失去訊號，會改為 FAIL。

## 3. 核心開發流程

本專案最重要的關係如下：

```text
project/test_armliteMcu/Kconfig
        |
        | qconf 存檔
        v
build/openrtos/test_armliteMcu/config.cmake
        |
        | ArmLite nested build 讀取同一份設定
        v
sdk/arm_lite_dev/armliteMcu/CMakeLists.txt
        |
        | 選擇 source + 產生 compile definitions
        v
armliteMcu.plugin -> armliteMcu.raw -> armliteMcu.hex
        |
        | #include "armliteMcu.hex"
        v
Host library 內的 gArmLiteMcuImage[]
        |
        | test_armlite_mcu.c: ITP_IOCTL_INIT
        v
iteRiscLoadData() -> iteRiscFireCpu()
        |
        v
sdk/arm_lite_dev/armliteMcu/main.c
```

### 3.1 Kconfig 如何使用

`project/test_armliteMcu/Kconfig` 是整個 Host 與 ArmLite firmware 的共用設定入口。開發者先在 **MCU Function Test -> Test Case** 選擇一個測試：

- `GPIOTEST`：啟用 `ARMLITE_GPIO_ENABLE` 與 `ARMLITE_GPIO_DEMO_ENABLE`。
- `UARTTEST`：啟用 `ARMLITE_UART_ENABLE` 與選定 UART port。
- `PWMTEST`：啟用 `ARMLITE_PWM_ENABLE`。
- `FREQUENCYTEST`：啟用 frequency capture 與所需 interrupt support。
- `INTERRUPTTEST`：啟用 ArmLite software interrupt self-test。
- `I2CTEST`：啟用 ArmLite I2C 與 Host loopback。
- `SARADCTEST`：啟用 SARADC。
- `ALLTEST`：同時啟用多個周邊，但不啟用 GPIO demo。

`ARMLITE_ENABLE` 與 `ARMLITE_MCU` 在此 project 為必要設定。ArmLite 與 ALT CPU 是不同的 core，不應因此測試額外啟用 ALT CPU。

Kconfig 的一般設定方式是由 test case 控制上層 enable，再由周邊設定指定腳位與參數。例如 GPIO TEST：

```text
GPIOTEST=y
  -> CFG_ARMLITE_GPIO_ENABLE=y
  -> CFG_ARMLITE_GPIO_DEMO_ENABLE=y
  -> CFG_ARMLITE_GPIO_PINS="42,43"
  -> CFG_ARMLITE_GPIO_DIR_PINS="42"
```

`CFG_ARMLITE_GPIO_PINS` 是 ArmLite 擁有的 GPIO，`CFG_ARMLITE_GPIO_DIR_PINS` 是其中的 output。以上例子表示 GPIO42 為 output、GPIO43 為 input。

qconf 存檔後，SDK 會產生：

```text
build/openrtos/test_armliteMcu/.config
build/openrtos/test_armliteMcu/config.cmake
```

Host build 使用這些 `CFG_*` 宏；ArmLite nested build 也會在 `sdk/arm_lite_dev/armliteMcu/CMakeLists.txt` include Host 的 `config.cmake`，因此兩邊使用同一份設定。

ArmLite CMake 不只是直接傳遞設定，還會：

1. 檢查 UART baud rate、GPIO 範圍與 RX/TX 衝突。
2. 將 UART0–UART5 與 PWM1–PWM8 轉成 bit mask。
3. 把 UART、PWM、frequency、SARADC 使用的 GPIO 自動加入 ArmLite ownership mask。
4. 檢查 output pin 必須同時存在於 owner pin 清單。
5. 依 `CFG_ARMLITE_*_ENABLE` 決定要編譯哪些 `mcu_*_test.c` 與周邊 driver。
6. 將計算後的值以 compile definition 傳入 firmware。

因此修改 Kconfig 後，必須重新建置 ArmLite firmware 與 Host image，不能只重新編譯 `test_armlite_mcu.c`。

### 3.2 Firmware 如何被建置與嵌入 Host

`sdk/arm_lite_dev/armliteMcu/CMakeLists.txt` 建置出 `armliteMcu.plugin` ELF，後續依序產生：

```text
armliteMcu.plugin  --objcopy-->  armliteMcu.raw
armliteMcu.raw     --dataconv--> armliteMcu.hex
```

Host driver `sdk/driver/arm_lite_dev/armliteMcu/armliteMcuDevice.c` 以下列方式把 firmware 編譯進 Host image：

```c
static uint8_t gArmLiteMcuImage[] =
{
#include "armliteMcu.hex"
};
```

這表示上板時不是從 filesystem 動態開啟 `.hex` 檔；firmware bytes 已經在建置期間嵌入 Host executable。

### 3.3 `test_armlite_mcu.c` 如何 load firmware

Host 的 `TestFunc()` 執行順序如下：

1. `ithGpioInitArmLitePins()`：在 ArmLite 啟動前，由 Host 設定專用 GPIO 的 mux、direction、initial level 與 pull。
2. `itpInit()`：初始化 ITP devices。
3. `armliteMcuReportWiring()`：列印當前 test case 的實際接線。
4. `ITP_IOCTL_ARMLITE_SWITCH_ENG`：將 ArmLite framework 切換到 `ARMLITE_MCU` device。如果原本是其他 engine，driver 會先 reset ArmLite CPU，再將後續 ioctl 指向 `itpDeviceArmLiteMcu`。
5. `ITP_IOCTL_INIT`：真正進行 firmware load 與 CPU start。
6. 啟用 memory dump 時，顯示 debug memory 與 backtrace address。
7. 進入 I2C loopback 或 `armliteMcuMonitor()` 讀取 telemetry。

`ITP_IOCTL_INIT` 會轉到 `armliteMcuDevice.c` 的 `armliteMcuIoctl()`，其實際流程是：

```text
iteRiscResetCpu(ARMLITE_CPU)
        |
        +-- 清理前一次的 UART/PWM/frequency/SARADC 狀態
        +-- 清除 telemetry shared memory
        +-- 保留 UART DMA channel 6/7（如有啟用）
        |
loadDeviceFirmware()
        |
        +-- iteRiscLoadData(ARMLITE_CPU_IMAGE_MEM_TARGET,
        |                   gArmLiteMcuImage, image_size)
        +-- 驗證 firmware header magic/target/API/load/end address
        +-- 寫入 cmdExchangeAddress
        +-- flush data cache/memory buffer
        |
清除 command request/response registers
        |
寫入 I2C/DMA boot resource（如有啟用）
        |
iteRiscFireCpu(ARMLITE_CPU)
```

`iteRiscLoadData()` 只負責將嵌入的 image 複製到 `ARMLITE_CPU_IMAGE_MEM_TARGET`；`iteRiscFireCpu()` 才是解除 reset 並讓 ArmLite 從 firmware entry point 開始執行。

### 3.4 ArmLite `main.c` 執行流程

ArmLite firmware 進入 `main()` 後依序執行：

1. **Debug 初始化**：如有 `CFG_ARMLITE_MCU_LOG_DUMP`，初始化 memory ring log；如有 backtrace，再初始化 exception dump area。
2. **Scheduler timer**：`startTimer(CFG_ARMLITE_SCHEDULER_TIMER)`，並以 `ARMLite_GetBusClock()` 取得 bus clock。
3. **Telemetry**：`armliteMcuTelemetryInit()` 寫入 active peripheral mask 與 bus clock。Host 會使用這個 mask 確認載入的 firmware 與 Host test case 一致。
4. **UART direct log**：依 Kconfig 設定初始化 log UART。
5. **Interrupt**：初始化 interrupt controller、探測可用 route，但此時還未開始 dispatch。
6. **Peripheral init**：`armliteMcuInit()` 依 `ARMLITE_MCU_PERIPHERAL` enum 逐一呼叫已編譯進來的 GPIO/I2C/UART/PWM/frequency/interrupt/SARADC/CAN init。
7. **Interrupt start**：所有周邊初始化完成後才啟用 interrupt dispatch。
8. **計算 1 ms tick**：由 bus clock 計算 `ticksPerUs` 與 `ticksPerMs`。
9. **進入永久迴圈**：等待下一個 tick，再呼叫 `armliteMcuOnTick()`。

```c
while (1)
{
    tickStart = armliteMcuWaitForNextTick(tickStart, ticksPerMs);
    armliteMcuOnTick();
}
```

`armliteMcuWaitForNextTick()` 在等待 1 ms 時不是單純 idle，它持續呼叫 `armliteMcuPoll()`，讓 UART 與 interrupt self-test 可以使用較細的時序。

`armliteMcuOnTick()` 每 1 ms 進行：

- frequency polling。
- `armliteMcuTask()` 週期排程。
- telemetry 內容與 heartbeat 更新。
- 每秒輸出 direct UART status（如有啟用）。
- 每 10 ms publish memory log（如有啟用）。

Task table 在編譯時由 `#ifdef` 決定內容：

| Peripheral task | 週期 |
| --- | ---: |
| GPIO demo | 10 ms |
| SARADC | 10 ms |
| I2C | 1 ms |
| CAN | 1 ms |

UART、frequency 與 interrupt test 不完全依賴這個 task table，它們有各自的 poll 或 interrupt 路徑。

## 4. 主要程式檔

### Host 端

- `project/test_armliteMcu/Kconfig`：測試項目、GPIO、UART、PWM、frequency、SARADC 與 debug 設定。
- `project/test_armliteMcu/test_armlite_mcu.c`：載入 firmware、報告接線、讀取 telemetry 與 PASS/FAIL 判定。
- `project/test_armliteMcu/test_armlite_i2c.c`：I2C loopback 測試。
- `project/test_armliteMcu/test_openrtos.c`：OpenRTOS 入口與測試 thread。

### ArmLite MCU 端

- `main.c`：Firmware 入口、scheduler 與周邊初始化。
- `mcu_*_test.c`：各測試的流程與 telemetry counter 更新。
- `gpio/`、`uart/`、`pwm/`、`frequency/`、`i2c/`、`saradc/`、`canbus/`：各周邊驅動實作。
- `telemetry/`：Host/MCU 共用的狀態發佈機制。
- `debug/`：memory ring log 與 exception backtrace。
- `log/`：ArmLite 直接 UART log。

## 5. 測試項目與接線

| Test Case | 功能 | 預設接線/操作 |
| --- | --- | --- |
| GPIO TEST | GPIO output/input loopback | GPIO42 output 接 GPIO43 input |
| UART TEST | UART loopback | GPIO45 TX 接 GPIO44 RX |
| PWM TEST | PWM output | 以示波器量測選定的 PWM GPIO |
| GPIO FREQUENCY TEST | GPIO frequency capture | 訊號源接 GPIO43 |
| INTERRUPT TEST | ArmLite software interrupt | 無需外部接線 |
| SARADC TEST | XAIN0–XAIN7 | 類比輸入預設使用 GPIO19–GPIO26 |
| ALL TEST | UART + PWM + frequency + SARADC | UART loopback、PWM-to-frequency loopback 與 SARADC 輸入 |

ALL TEST 不包含 GPIO demo，避免 demo 切換已經由其他周邊佔用的 GPIO。實際 GPIO 號碼以當前 Kconfig 為準，不一定是上表的預設值。

## 6. Kconfig 設定快速參考

本節以 `sdk/Kconfig` 的 ArmLite MCU 設定為主，`project/test_armliteMcu/Kconfig` 則負責依 test case 提供 project default。在 qconf 的 **MCU Function Test -> Test Case** 選擇測試後，再到 ArmLite 周邊選單確認對應參數。

以下表格使用 Kconfig 原始 symbol 名稱，例如 `ARMLITE_UART_ENABLE`；它在 C/C++ compile definition 與產生的 `.config` 中會以 `CFG_ARMLITE_UART_ENABLE` 形式出現。

### 6.1 核心與共用設定

| Config | 預設 | 說明 |
| --- | --- | --- |
| `ARMLITE_ENABLE` | `n` | 啟用 ArmLite core；與 ALT CPU 無關，兩者是不同 core。 |
| `ARMLITE_MCU` | `n` | 建置並載入基本 ArmLite MCU firmware，依賴 `ARMLITE_ENABLE`。 |
| `ARMLITE_MCU_LOG_DUMP` | `n` | 將 ring log 寫入 shared debug memory，可由 glamomem 讀取。 |
| `ARMLITE_MCU_BACKTRACE_DUMP` | `n` | 依賴 log dump；將 exception registers/backtrace 放在 8 KiB debug region 的最後 1 KiB。 |
| `ARMLITE_TELEMETRY_ENABLE` | 內部值 | 無 qconf prompt，由需要回報結果的周邊自動 `select`。 |
| `ARMLITE_INTERRUPT_ENABLE` | 內部值 | 無 qconf prompt，由 UART interrupt mode、frequency detect 或 interrupt test 自動 `select`。 |

`ARMLITE_INTERRUPT_ENABLE` 會啟用 FA626TE IRQ entry 與 ArmLite ITINT dispatcher。Firmware 會探測 controller-1 `+0x080` 與 controller-2 `+0x140` 路徑；探測與 UART interrupt mode 運作時，ALT CPU 必須保持停用。

### 6.2 GPIO ownership 與初始化

| Config | 預設 | 用途 |
| --- | --- | --- |
| `ARMLITE_GPIO_ENABLE` | `n` | 啟用 ArmLite GPIO reservation 與 Host 預先初始化。 |
| `ARMLITE_GPIO_DEMO_ENABLE` | `n` | 切換第一個 owned output，取樣第一個 owned input，並自動啟用 telemetry。 |
| `ARMLITE_GPIO_PINS` | `""` | 以逗號分隔的 ArmLite owner pin，例如 `"42,43"`。 |
| `ARMLITE_GPIO_DIR_PINS` | `""` | owner pins 中的 output；必須是 `GPIO_PINS` 子集。 |
| `ARMLITE_GPIO_INIT_HIGH_PINS` | `""` | output pins 中初始為 high 的腳位；其餘初始為 low。 |
| `ARMLITE_GPIO_PULL_ENABLE_PINS` | `""` | 啟用 internal pull resistor 的 owned pins。 |
| `ARMLITE_GPIO_PULL_UP_PINS` | `""` | pull-enabled pins 中使用 pull-up 者；其餘為 pull-down。 |

Host 在 firmware load 前的 `ithGpioInitArmLitePins()` 會對最終 ownership mask 中的 GPIO 進行：

1. 將 pin mux 設為 GPIO mode 0。
2. 依 direction mask 設為 input/output。
3. output 先寫入 initial level，再開啟 output direction，避免不必要的 glitch。
4. input 依 pull-enable 與 pull-up mask 設定電阻。

最終 ownership 不只包含 `ARMLITE_GPIO_PINS`。ArmLite CMake 會自動加入：

- 已啟用 UART 的 RX/TX；TX 同時加入 output mask。
- direct UART log 的 TX；同時加入 output mask。
- 已啟用 PWM channel 的 GPIO；同時加入 output mask。
- frequency detect GPIO；作為 input。
- SARADC XAIN0–XAIN7 GPIO；作為 input。

上述 pin 會被轉成 bank0–bank3 的 `CFG_ARMLITE_GPIO_BANKx_MASK` 與 `DIR_MASK`，同時傳給 Host GPIO driver 與 ArmLite firmware。Firmware 啟動後，Host 不應再設定或驅動這些腳位。

### 6.3 I2C

| Config | 預設 | 說明 |
| --- | ---: | --- |
| `ARMLITE_I2C_ENABLE` | `n` | 將 ArmLite I2C 功能編譯進 firmware。 |
| `ARMLITE_I2C_PORT` | `2` | ArmLite 使用的 I2C controller port。 |
| `GPIO_ARMLITE_I2C_SCL` | `40` | SCL GPIO。 |
| `GPIO_ARMLITE_I2C_SDA` | `41` | SDA GPIO。 |
| `ARMLITE_I2C_CLOCK_HZ` | `400000` | I2C bus clock，單位 Hz。 |
| `ARMLITE_I2C_DELAY` | `0` | SDA hold time/TSR 設定。 |

I2C 的 SCL/SDA 有專用 config，不是 GPIO demo 的 input/output pin。泛用 `sdk/Kconfig` 的 `ARMLITE_I2C_ENABLE` 本身不會 `select ARMLITE_GPIO_ENABLE`，也不會由 GPIO ownership CMake 自動併入 `ARMLITE_GPIO_PINS`；使用時仍必須確認 Host 沒有同時使用該 I2C controller 與 pin mux。`test_armliteMcu` 的 I2C loopback 還會設定 Host 端對應 I2C 接口，實際接線以 project Kconfig 為準。

### 6.4 UART0–UART5

`ARMLITE_UART_ENABLE` 是總開關，會自動啟用 telemetry 與 GPIO reservation。再以 `ARMLITE_UARTn_ENABLE` 選擇一個或多個 controller；UART0 預設啟用，UART1–UART5 預設停用。Host CPU 不可同時使用 ArmLite 已 claim 的 UART controller。

每個 UART 都有相同三組設定：

| Config pattern | 說明 |
| --- | --- |
| `ARMLITE_UARTn_ENABLE` | 將 UARTn 交給 ArmLite。 |
| `ARMLITE_UARTn_FIFO` | 預設 mode，firmware polling UART FIFO。 |
| `ARMLITE_UARTn_INTR` | Interrupt mode，自動 `select ARMLITE_INTERRUPT_ENABLE`。 |
| `ARMLITE_UARTn_DMA` | DMA mode，固定使用 DMA channel 6/7。 |
| `ARMLITE_UARTn_IO_MODE` | 內部整數：FIFO=`0`、interrupt=`1`、DMA=`2`。 |
| `ARMLITE_UARTn_BAUDRATE` | 預設 `115200`，建置檢查範圍 1200–3000000。 |
| `GPIO_ARMLITE_UARTn_RX` | UARTn RX input GPIO。 |
| `GPIO_ARMLITE_UARTn_TX` | UARTn TX output GPIO；也可單獨供 direct log 使用。 |

UART GPIO 預設值：

| UART | RX | TX |
| --- | ---: | ---: |
| UART0 | GPIO44 | GPIO45 |
| UART1 | `-1` | `-1` |
| UART2 | `-1` | `-1` |
| UART3 | `-1` | `-1` |
| UART4 | `-1` | `-1` |
| UART5 | `-1` | `-1` |

UART1–UART5 的 `-1` 是「尚未設定」，啟用前必須改為 0–127 的有效 GPIO，且 RX/TX 不可相同。同一時間最多只允許一個 ArmLite UART 使用 DMA mode。UART0 interrupt mode 會實驗性地探測 controller-2 source 38；硬體路徑未被證實時會安全地初始化失敗。

### 6.5 ArmLite direct UART log

| Config | 預設 | 說明 |
| --- | --- | --- |
| `ARMLITE_MCU_UART_LOG_ENABLE` | `n` | ArmLite 直接從專用 UART TX 輸出 log，不經 Host CPU。 |
| `ARMLITE_MCU_UART_LOG_UART0`–`UART5` | choice | 只能選擇一個未被 ArmLite UART test 佔用的 controller。 |
| `ARMLITE_MCU_UART_LOG_UART_PORT` | 內部值 | 依 choice 轉成 port 0–5。 |
| `ARMLITE_MCU_UART_LOG_BAUDRATE` | `115200` | 有效範圍 1200–3000000。 |

Direct log 會自動啟用 GPIO reservation，並將選定 UART 的 `GPIO_ARMLITE_UARTn_TX` 加入 owner/output mask。該 UART 不可同時用於 ArmLite UART loopback 或 Host console。`ARMLITE_UART_CLAIM_UART0`–`UART5` 為內部組合值，用來隱藏或阻止 Host 端重複使用同一 controller。

### 6.6 PWM

| Config | 預設 | 說明 |
| --- | ---: | --- |
| `ARMLITE_PWM_ENABLE` | `n` | PWM 總開關，自動啟用 telemetry 與 GPIO reservation。 |
| `ARMLITE_PWM1_ENABLE` | `y` | 保留 PWM1 timer channel。 |
| `ARMLITE_PWM2_ENABLE`–`PWM8_ENABLE` | `n` | 保留額外 PWM channel。 |
| `ARMLITE_PWM_CHANNEL` | `1` | 真正輸出測試波形的 channel，必須是已啟用 channel，且不可與 `HW_TIMER` 相同。 |
| `ARMLITE_PWM_FREQUENCY` | `1000` | PWM output frequency，單位 Hz。 |
| `ARMLITE_PWM_DUTY` | `50` | Duty cycle，有效範圍 1–99%。 |
| `GPIO_ARMLITE_PWM1` | `42` | PWM1 output GPIO。 |
| `GPIO_ARMLITE_PWM2`–`PWM8` | `-1` | 啟用對應 channel 前必須設為有效 GPIO。 |

啟用某個 PWM channel 會保留它的 timer 與 GPIO，但只有 `ARMLITE_PWM_CHANNEL` 指定的單一 channel 會由 test firmware 輸出波形。所有已啟用 PWM GPIO 都會自動加入 owner/output mask。

### 6.7 GPIO frequency detect

| Config | 預設 | 說明 |
| --- | ---: | --- |
| `ARMLITE_FREQUENCY_ENABLE` | `n` | 啟用 GPIO rising-edge frequency measurement，並自動啟用 telemetry、GPIO 與 interrupt。 |
| `ARMLITE_FREQUENCY_EXPECTED_HZ` | `1000` | Host PASS/FAIL 的預期值，範圍 1–3000 Hz，Host 容許 5% 誤差。 |
| `ARMLITE_FREQUENCY_MAX_HZ` | `3000` | 高於此頻率的 edge period 會被拒絕。 |
| `ARMLITE_FREQUENCY_TIMEOUT_MS` | `2000` | 超過此時間沒有 rising edge 時判為 no signal。 |
| `GPIO_ARMLITE_FREQUENCY` | `43` | rising-edge input GPIO，自動加入 owner mask，方向為 input。 |

與 PWM 一起使用時，可將選定 PWM GPIO 接到 frequency GPIO。PWM frequency 必須與 expected frequency 一致，而 output GPIO 與 frequency input GPIO 不可使用同一支 pin。

### 6.8 Interrupt self-test

| Config | 預設 | 說明 |
| --- | --- | --- |
| `ARMLITE_INTERRUPT_TEST_ENABLE` | `n` | 自動 `select ARMLITE_INTERRUPT_ENABLE`，週期觸發已探測到的 software interrupt bank。 |

此測試無需 GPIO。Telemetry 會報告 pass/fail、dispatch、unexpected interrupt 與 budget-exhaust counter。建議在啟用 UART interrupt mode 前，先單獨執行此測試確認 interrupt route。

### 6.9 SARADC

| Config | 預設 | 說明 |
| --- | ---: | --- |
| `ARMLITE_SARADC_ENABLE` | `n` | 僅限 IT9860 family；將 SARADC controller 交給 ArmLite，自動啟用 telemetry 與 GPIO reservation。 |
| `ARMLITE_SARADC_VALID_XAIN` | `0xff` | XAIN0–XAIN7 有效 bit mask；bit0 對應 XAIN0。 |
| `ARMLITE_SARADC_CLOCK_DIVIDER` | `9` | SARADC clock divider，有效範圍 0–31。 |

XAIN GPIO 預設如下：

| XAIN | GPIO | XAIN | GPIO |
| --- | ---: | --- | ---: |
| XAIN0 | 19 | XAIN4 | 23 |
| XAIN1 | 20 | XAIN5 | 24 |
| XAIN2 | 21 | XAIN6 | 25 |
| XAIN3 | 22 | XAIN7 | 26 |

對應 config 為 `GPIO_ARMLITE_XAIN_0`–`GPIO_ARMLITE_XAIN_7`。這些 pin 會自動加入 owner mask，方向視為 input。ArmLite 使用 SARADC 時，Host CPU 不可同時啟用或存取正常 SARADC driver。

### 6.10 CAN bus

| Config | 預設 | 說明 |
| --- | ---: | --- |
| `ARMLITE_CANBUS_ENABLE` | `n` | 將 CANBUS1 交給 ArmLite；Host 不可同時使用 CANBUS1。 |
| `ARMLITE_CANBUS_FD_ENABLE` | `n` | 僅支援 IT9866_AT/9867_AT/9868_AT/9869_AT package。 |
| `GPIO_ARMLITE_CANBUS_RX` | `-1` | CAN RX GPIO，啟用前應設為有效腳位。 |
| `GPIO_ARMLITE_CANBUS_TX` | `-1` | CAN TX GPIO，啟用前應設為有效腳位。 |

CAN config 本身不會在 `sdk/Kconfig` 自動 `select ARMLITE_GPIO_ENABLE`，而目前 ArmLite GPIO ownership CMake 也沒有像 UART/PWM 一樣自動併入 CAN RX/TX。因此開發者必須額外確認 Host pin mux/driver 不會跟 CAN1 及這兩支 GPIO 衝突。若 project 要以通用 ownership 機制保護它們，可同時啟用 `ARMLITE_GPIO_ENABLE` 並將 RX/TX 列入 `ARMLITE_GPIO_PINS`，其中 TX 列入 `ARMLITE_GPIO_DIR_PINS`；但要確認這與 CAN driver 所需 mux 初始化順序一致。

### 6.11 存檔與建置檢查

存檔後應確認：

```text
build/openrtos/test_armliteMcu/.config
build/openrtos/test_armliteMcu/config.cmake
```

已更新為新設定。新增或修改 Kconfig symbol 後，必須重新開啟 qconf 並存檔，然後重新建置 ArmLite firmware 與 Host image。只有需要完全重置設定時，才刪除 `.config` 與 `config.cmake`。

建置期間會檢查常見錯誤，包含無效 GPIO、UART RX/TX 相同、多個 UART 同時使用 DMA、PWM test channel 未保留、PWM 與 scheduler timer 衝突、frequency input 與 PWM output 同 pin，以及 direction pin 不屬於 owner pin 等問題。

## 7. 建置與執行

在 SDK 的 `build/openrtos` 目錄執行：

```powershell
.\test_armliteMcu.cmd
```

建置會先以 Host project 的 `config.cmake` 建置 ArmLite firmware，產生 `armliteMcu.plugin`、`armliteMcu.raw`、`armliteMcu.hex`、disassembly 與 symbol 檔，再將 firmware 嵌入 Host image。

建置完成後執行：

```powershell
.\test_armliteMcu\project\test_armliteMcu\run.cmd
```

`run.cmd` 會透過 glamomem 將 RAM image 載入開發板。若更換不同 DDR clock 的板子，必須在 Kconfig 同時調整 `RAM_INIT_SCRIPT` 與 `DBG_INIT_SCRIPT`。

## 8. 正常執行確認

Host console 應顯示測試名稱、實際接線、firmware bus clock、active peripheral mask 與各項 counter。檢查重點：

1. `heartbeat` 持續增加，表示 ArmLite scheduler 正常運作。
2. Firmware active mask 與 Host 編譯的 test case 相同。
3. 對應測試的 toggle/read/edge/UART/SARADC counter 持續增加。
4. error、mismatch、unexpected interrupt 與 budget-exhaust counter 沒有異常增加。
5. 連續三次健康 snapshot 後，Host 顯示 PASS。

## 9. GPIO demo 注意事項

GPIO demo 會找到第一個 owned output pin 作為輸出，並找到第一個 owned input pin 作為輸入，每 10 ms 切換輸出並取樣輸入。必須將 output 實體接到 input，loopback match count 才會增加。

`ithGpioGet(pin)` 回傳的是所在 bank 的 bit mask，不是固定的 0/1。顯示單一 GPIO level 時應寫成：

```c
printf("GPIO%d = %u\n", pin, ithGpioGet(pin) ? 1U : 0U);
```

請避免以 GPIO 切換週期的整數倍進行輪詢。例如 MCU 每 10 ms 切換，Host 固定每 100 ms 讀取，可能每次都取到相同相位而誤以為 GPIO 沒有變化。可改為 17 ms 等非同步週期，或以 telemetry toggle/match counter 進行驗證。

## 10. UART log

啟用 `CFG_ARMLITE_MCU_UART_LOG_ENABLE` 後，ArmLite 可直接從選定 UART TX 輸出啟動設定與每秒狀態。預設為 UART0 TX GPIO45、115200 8N1。

該 UART controller 與 TX pin 不可同時由 Host console、UART loopback 或其他功能使用。UART TEST 或 ALL TEST 佔用 UART0 時，應將 direct log 移至其他未使用 UART，並正確設定其 TX GPIO。

## 11. Memory log 讀取

需先在 Kconfig 啟用 `CFG_ARMLITE_MCU_LOG_DUMP`。Host 啟動時會顯示類似：

```text
[ArmLite MCU] debug memory: 0xXXXXXXXX
```

進入 `tool/bin/PrintArmLiteMcuDump` 後執行：

```powershell
python .\print_armlite_mcu_log.py `
  --addr 0xXXXXXXXX `
  --target D:\path\to\glamomem.dat
```

`--target` 必須使用目前 project 對應的 `glamomem.dat`。常用選項：

```powershell
# 顯示 ring buffer 現有內容後持續監看
python .\print_armlite_mcu_log.py --addr 0xXXXXXXXX `
  --target .\glamomem.dat --print-initial

# 只顯示現有內容後結束
python .\print_armlite_mcu_log.py --addr 0xXXXXXXXX `
  --target .\glamomem.dat --print-initial --no-loop
```

預設每 100 ms 輪詢。若每兩個字元的順序互換，加入 `--word-endian big`。

## 12. Exception backtrace

啟用 `CFG_ARMLITE_MCU_BACKTRACE_DUMP` 後，8 KB debug region 的前 7 KB 為 log，最後 1 KB 為 exception backtrace。Host 會另外顯示：

```text
[ArmLite MCU] backtrace address: 0xYYYYYYYY
```

先將 Arm GNU toolchain 加入當前 PowerShell 的 PATH：

```powershell
$env:Path = "C:\ite-toolchain\arm-none-eabi\bin;$env:Path"
arm-none-eabi-addr2line --version
```

發生 Undefined instruction、Prefetch abort 或 Data abort 後執行：

```powershell
python .\print_armlite_mcu_backtrace.py `
  --addr 0xYYYYYYYY `
  --target D:\path\to\glamomem.dat `
  --elf D:\path\to\armliteMcu.plugin
```

`--addr` 必須是 backtrace address，不是 log address。`armliteMcu.plugin` 必須與板上執行的 firmware 完全相同，否則 function 與 source line 會解析錯誤。加入 `--loop` 可持續等待新的 exception dump。

## 13. 常見問題

### Heartbeat 不增加

- 確認 firmware load 沒有失敗。
- 確認 DDR init script 與板子 clock 一致。
- 用 memory log 檢查 firmware 是否已啟動。
- 啟用 backtrace，檢查是否發生 exception。

### GPIO 有 toggle count，但測試不 PASS

- 確認實體連接的是輸出與輸入腳，而非兩個 output。
- 確認 `CFG_ARMLITE_GPIO_DIR_PINS` 與實際接線一致。
- 確認 pin mux 是 GPIO mode 0，沒有被其他周邊重複佔用。
- 檢查 loopback match/mismatch counter，不要只看單次 GPIO 輪詢值。

### UART loopback 失敗

- 確認 TX/RX 交叉接線、baud rate 與 8N1 設定。
- 確認 UART controller/GPIO 沒有被 Host console 或 direct log 佔用。
- Interrupt mode 需先確認 ArmLite interrupt route，且 ALT CPU 必須停用。
- DMA mode 固定使用 DMA channel 6/7，同一時間只能有一個 ArmLite UART 使用 DMA。

### Dump 工具報 invalid magic 或無法讀取

- 使用 Host console 當次顯示的實際 address。
- 確認 `--target` 不是其他 project 的 `glamomem.dat`。
- 確認 `CFG_ARMLITE_MCU_LOG_DUMP` 已啟用，且 firmware 已執行 `armliteMcuLogInit()`。
- Backtrace 只會在 exception 發生後出現，並必須使用相同 build 的 ELF。

## 14. 開發建議

1. 一次先啟用一個 test case，確認基本功能後再使用 ALL TEST。
2. 修改周邊腳位時，同時檢查 GPIO ownership、direction、pin mux 與 Host console。
3. 保留每次上板 image 對應的 `armliteMcu.plugin`，以便之後解析 backtrace。
4. 判斷系統健康時，優先使用 telemetry counter，單次的 GPIO 或 register 讀值只作為輔助資訊。
