ci.sh 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. #!/bin/bash
  2. set -euo pipefail
  3. PROJECT="$(cd "$(dirname "$0")" && pwd)"
  4. LOG_DIR="$PROJECT/log"
  5. TD_BIN="${TD_BIN:-/root/TD_4.6.7/TD_4.6.7_Linux/bin/td}"
  6. UART_DEV="${UART_DEV:-/dev/ttyUSB0}"
  7. TRACE_TIMEOUT="${TRACE_TIMEOUT:-10}"
  8. TRACE_MIN_SNAPSHOTS="${TRACE_MIN_SNAPSHOTS:-10}"
  9. LOCK_FILE="$PROJECT/.ci.lock"
  10. for cmd in python3 iverilog vvp flock sha256sum awk tee stty; do
  11. command -v "$cmd" >/dev/null 2>&1 || {
  12. echo "ERROR: missing command: $cmd"
  13. exit 2
  14. }
  15. done
  16. [ -x "$TD_BIN" ] || {
  17. echo "ERROR: TD not executable: $TD_BIN"
  18. exit 2
  19. }
  20. [ -e "$UART_DEV" ] || {
  21. echo "ERROR: UART device missing: $UART_DEV"
  22. exit 2
  23. }
  24. [ -f "$PROJECT/scripts/run_test.sh" ] || {
  25. echo "ERROR: missing scripts/run_test.sh"
  26. exit 2
  27. }
  28. [ -f "$PROJECT/scripts/build.sh" ] || {
  29. echo "ERROR: missing scripts/build.sh"
  30. exit 2
  31. }
  32. [ -f "$PROJECT/scripts/program.sh" ] || {
  33. echo "ERROR: missing scripts/program.sh"
  34. exit 2
  35. }
  36. [ -f "$PROJECT/host/reference_selftest.py" ] || {
  37. echo "ERROR: missing host/reference_selftest.py"
  38. exit 2
  39. }
  40. [ -f "$PROJECT/host/trace_ci_check.py" ] || {
  41. echo "ERROR: missing host/trace_ci_check.py"
  42. exit 2
  43. }
  44. # 防止两个 CI 同时操作同一块 FPGA
  45. exec 9>"$LOCK_FILE"
  46. flock -w 300 9 || {
  47. echo "ERROR: FPGA CI is already running"
  48. exit 3
  49. }
  50. # log/ 永远只代表最近一次 CI
  51. rm -rf "$LOG_DIR"
  52. mkdir -p "$LOG_DIR"
  53. # 清理 TD 以前自动产生的时间戳日志
  54. rm -f "$PROJECT"/td_*.log
  55. STARTED="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
  56. GIT_SHA="LOCAL-NOT-GIT"
  57. GIT_DIRTY="unknown"
  58. if command -v git >/dev/null 2>&1 &&
  59. git -C "$PROJECT" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
  60. GIT_SHA="$(git -C "$PROJECT" rev-parse HEAD)"
  61. if [ -n "$(git -C "$PROJECT" status --porcelain)" ]; then
  62. GIT_DIRTY="yes"
  63. else
  64. GIT_DIRTY="no"
  65. fi
  66. fi
  67. echo "=============================================="
  68. echo " FPGA PWM TRACE CI"
  69. echo "=============================================="
  70. echo "Project : $PROJECT"
  71. echo "TD : $TD_BIN"
  72. echo "UART : $UART_DEV"
  73. echo "Log : $LOG_DIR"
  74. echo
  75. set +e
  76. (
  77. set -euo pipefail
  78. echo "=============================================="
  79. echo "STAGE 1: TESTBENCH"
  80. echo "=============================================="
  81. cd "$PROJECT"
  82. bash scripts/run_test.sh
  83. echo
  84. echo "=============================================="
  85. echo "STAGE 2: REFERENCE SELFTEST"
  86. echo "=============================================="
  87. python3 host/reference_selftest.py
  88. echo
  89. echo "=============================================="
  90. echo "STAGE 3: FPGA BUILD"
  91. echo "=============================================="
  92. TD_BIN="$TD_BIN" bash scripts/build.sh
  93. echo
  94. echo "=============================================="
  95. echo "STAGE 4: FPGA PROGRAM"
  96. echo "=============================================="
  97. TD_BIN="$TD_BIN" bash scripts/program.sh
  98. echo
  99. echo "=============================================="
  100. echo "STAGE 5: UART TRACE"
  101. echo "=============================================="
  102. sleep 2
  103. echo "Configuring UART: $UART_DEV @ 115200 8N1 raw"
  104. stty -F "$UART_DEV" \
  105. raw -echo \
  106. 115200 cs8 -cstopb -parenb \
  107. -ixon -ixoff -crtscts
  108. python3 host/trace_ci_check.py \
  109. "$UART_DEV" \
  110. --timeout "$TRACE_TIMEOUT" \
  111. --min-snapshots "$TRACE_MIN_SNAPSHOTS"
  112. ) 2>&1 | tee "$LOG_DIR/ci.log"
  113. RC=${PIPESTATUS[0]}
  114. set -e
  115. BIT="$PROJECT/build/td/fpga_pwm_trace.bit"
  116. BIT_SHA256="N/A"
  117. if [ -s "$BIT" ]; then
  118. BIT_SHA256="$(sha256sum "$BIT" | awk '{print $1}')"
  119. fi
  120. ENDED="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
  121. if [ "$RC" -eq 0 ]; then
  122. RESULT="PASS"
  123. else
  124. RESULT="FAIL"
  125. fi
  126. cat > "$LOG_DIR/metadata.txt" <<META
  127. project=$PROJECT
  128. git_sha=$GIT_SHA
  129. git_dirty=$GIT_DIRTY
  130. host=$(hostname)
  131. td_bin=$TD_BIN
  132. uart_dev=$UART_DEV
  133. bit=$BIT
  134. bit_sha256=$BIT_SHA256
  135. started=$STARTED
  136. ended=$ENDED
  137. exit_code=$RC
  138. result=$RESULT
  139. META
  140. # TD 每次运行自己产生的 td_时间.log 不保留
  141. # 完整 CI 输出已经统一保存在 log/ci.log
  142. rm -f "$PROJECT"/td_*.log
  143. echo
  144. echo "=============================================="
  145. if [ "$RC" -eq 0 ]; then
  146. echo " CI PASS"
  147. else
  148. echo " CI FAIL"
  149. fi
  150. echo "=============================================="
  151. echo "Result : $RESULT"
  152. echo "Log : $LOG_DIR/ci.log"
  153. echo "Meta : $LOG_DIR/metadata.txt"
  154. echo "Bit : $BIT"
  155. exit "$RC"