Showing Posts From
양산
- 28 Dec, 2025
토요일에도 회사, 양산 준비 열기 1주일 남음
토요일에도 회사, 양산 준비 열기 1주일 남음 오전 10시, 토요일 출근 알람 없이 일어났다. 7시 반. 토요일인데 몸이 알아서 깬다. 평소 같으면 "토요일은 무슨"이라고 했을 거다. 이번은 다르다. 양산 D-7. 팀장이 어제 말했다. "내일 나올 수 있는 사람?" 아무도 대답 안 했다. 5초쯤 지나서. "전 나오겠습니다." 내가 먼저 말했다. 그러니까 다들 나온다고 했다. 5명 전원. 씻고 나왔다. 회사 도착 9시 50분. 주차장이 텅 비었다. 우리 팀 차만 5대.주차장에서 코드 리뷰 사무실 들어가기 전에 차에 앉았다. 노트북 켰다. 어젯밤 커밋한 코드. DMA 버퍼 오버플로우 체크 로직. 주차장에서 다시 봤다. if(dma_buffer_idx >= DMA_BUFFER_SIZE) { // 여기서 뭘 해야 하지 }주석이 이렇게 되어 있다. 어젯밤 새벽 2시에 쓴 거다. 뭘 생각했는지 기억이 안 난다. 10분 동안 봤다. 아, 인터럽트 끄고 리셋해야지. 노트북에 메모했다. 사무실 가서 고치자. 차에서 내렸다. 팀장 차도 와 있다. 회의실에 모인 5명 다들 와 있었다. 회의실에 노트북 펼쳐놓고. "커피 타올게요." 막내가 말했다. "나도." "저도요." 결국 5잔. 팀장이 화이트보드에 썼다.UART 타임아웃 이슈 DMA 버퍼 관리 저전력 모드 전류 초과 OTA 검증 미완4개. 1주일에 4개. "하나씩 가자. UART부터." 시작했다.UART 타임아웃, 그놈의 9600bps UART 통신이 가끔 멈춘다. 재현이 안 된다. 100번 중 1번꼴. "타임아웃 값이 문제 아닐까요?" "이미 늘렸어요. 1초로." "그럼 보레이트?" "9600으로 고정이에요. 센서 스펙." 센서 스펙. 저가 센서라 9600bps 고정이다. 바꿀 수가 없다. 오실로스코ープ 가져왔다. 실제 파형 봤다. Start 비트는 정상. Data 비트도 정상. Stop 비트에서 가끔 글리치. "하드웨어 문제 아닐까요?" 내가 물었다. "PCB는 검증 끝났어요." HW 팀장 답변. 그럼 뭐지. 점심때까지 봤다. 결론: 일단 리트라이 로직 강화. 근본 원인은 모른다. 치킨과 디버깅 점심은 치킨. 토요일이니까 치킨. 회사 앞 치킨집. "양산 나가면 괜찮을까요?" 막내가 물었다. "괜찮아야지." 팀장이 웃었다. 웃긴데 안 웃겼다. 치킨 먹으면서도 코드 봤다. 노트북 옆에 치킨. 키보드에 기름 묻었다. 휴지로 닦았다. "DMA 버퍼는 제가 오늘 고칠게요." 내가 말했다. "저는 저전력 모드 볼게요." 선배가 말했다. 역할 분담 끝. 치킨 다 먹었다. 오후 2시.DMA 버퍼 수정, 인터럽트 지옥 사무실로 돌아왔다. 에어컨 빵빵하다. 토요일이라 전기 아깝지 않은가 봐. DMA 버퍼 코드 열었다. 아침에 본 그거. void DMA_IRQHandler(void) { if(dma_buffer_idx >= DMA_BUFFER_SIZE) { __disable_irq(); dma_buffer_idx = 0; memset(dma_buffer, 0, DMA_BUFFER_SIZE); __enable_irq(); error_count++; } }이렇게 고쳤다. 빌드. 보드에 올렸다. 테스트 시작. 1분. 정상. 5분. 정상. 10분. 정상. 좋아. 30분째. 에러 로그 떴다. error_count: 1 뭐지. 다시 코드 봤다. 인터럽트 안에서 memset? 이게 문제인가? 수정. volatile uint8_t buffer_reset_flag = 0;void DMA_IRQHandler(void) { if(dma_buffer_idx >= DMA_BUFFER_SIZE) { buffer_reset_flag = 1; error_count++; } }// 메인 루프에서 if(buffer_reset_flag) { __disable_irq(); dma_buffer_idx = 0; memset(dma_buffer, 0, DMA_BUFFER_SIZE); buffer_reset_flag = 0; __enable_irq(); }다시 빌드. 다시 올렸다. 오후 4시. 저전력 모드, 18mA의 비극 선배가 소리쳤다. "이거 봐요!" 멀티미터 들고 있다. "18mA요." 스펙은 10mA 이하. 거의 두 배. "어디서 새는 거예요?" "모르겠어요. LED 다 껐는데." 회로도 펼쳤다. A4 용지 10장. 하나씩 봤다.MCU 슬립 모드: OK 센서 파워다운: OK 통신 모듈 오프: OK그럼? "풀업 저항 아닐까요?" 내가 말했다. "다 확인했어요." 30분 더 봤다. 발견했다. ADC 클럭. 슬립 모드에서도 안 꺼져 있다. 레지스터 하나 놓쳤다. RCC->APB2ENR &= ~RCC_APB2ENR_ADC1EN;이 한 줄. 안 들어가 있었다. 추가하고 측정. 9.2mA. 성공. 오후 6시. 저녁 7시, 아직 2개 남음 저녁은 편의점. 컵라면이랑 삼각김밥. 회의실에서 먹었다. 화이트보드 봤다.UART 타임아웃 이슈 (임시 해결) DMA 버퍼 관리 (완료) 저전력 모드 전류 초과 (완료) OTA 검증 미완하나 남았다. 제일 큰 거. OTA. Over The Air 펌웨어 업데이트. 양산 나가면 필드 업데이트 필요하다. 근데 검증이 덜 됐다. "부트로더 점프 부분 확인해 봤어요?" 팀장이 물었다. "네, 그건 되는데요." 막내가 답했다. "뭐가 문제?" "CRC 체크가 가끔 실패해요." CRC. 펌웨어 무결성 검사. 이게 실패하면 벽돌. "가끔?" "네. 100번 중 3번 정도." 100번 중 3번. 3%면 적은 건가? 아니다. 양산 1만 개면 300개 벽돌. "오늘 못 고치면?" "월요일에 해야죠." 팀장 얼굴이 어둡다. 다들 어둡다. OTA, 끝나지 않는 밤 CRC 실패 원인 찾기. 로그 뽑았다. Flash Write: OK Flash Read: OK CRC Calculate: 0xA3B5C2D1 CRC Expected: 0xA3B5C2D1 Result: PASS이렇게 나올 때도 있고. Flash Write: OK Flash Read: OK CRC Calculate: 0xA3B5C2D1 CRC Expected: 0xA3B5C2D8 Result: FAIL이렇게 나올 때도 있다. Expected 값이 달라진다. "플래시에 쓸 때 문제인가?" "근데 Flash Read는 OK잖아요." 코드 다시 봤다. 플래시 쓰기 부분. for(int i = 0; i < fw_size; i += 4) { HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, FLASH_APP_ADDR + i, *(uint32_t*)(fw_buffer + i)); }뭔가 이상하다. 플래시 쓰기 후 검증이 없다. 추가했다. uint32_t written_data = *(uint32_t*)(FLASH_APP_ADDR + i); uint32_t expected_data = *(uint32_t*)(fw_buffer + i); if(written_data != expected_data) { // 에러 처리 }다시 돌렸다. 100번. 실패 0번. 됐다. 오후 9시 반. 퇴근, 토요일 밤 "오늘 고생했습니다." 팀장이 말했다. "수고하셨어요." 다들 인사. 짐 챙겼다. 노트북, 충전기, 텀블러. 주차장 나왔다. 여전히 우리 차만 5대. 시동 걸었다. 라디오 켰다. 토요일 밤 음악 프로그램. 집 가는 길. 10분. 편하다. 양산까지 7일. 월요일부터 또 전쟁. 근데 오늘은 괜찮았다. 문제 3개 해결했다. 팀원들이랑 같이. 토요일에 회사 나온 게 아깝지 않다. 이상하게. 집 도착. 현관문 열고. 침대에 누웠다. 내일은 쉰다. 진짜 쉰다.월요일이 두렵지만, 오늘은 잘했다.
- 02 Dec, 2025
양산 D-7, 잠을 잘 수 없다
양산 D-7, 잠을 잘 수 없다 알람이 울린다. 5시 30분. 어제는 3시간 잤다. 그 전날은 2시간. 통패턴이다. 일주일 수면 시간을 계산하지 않는 게 정신 건강에 좋다. 침대에 누웠는데 눈이 감기지 않는다. 천장을 본다. 까만 천장. 거기에 코드가 보인다. if (sensor_value > THRESHOLD) { // 여기 로직이 맞나? }맞다. 틀렸다. 아니, 모르겠다. 일단 일어난다.D-7이라는 숫자 양산까지 일주일. 정확히는 7일. 168시간. 10,080분. 더는 세지 않는다. 세면 더 짧아 보인다. 오늘 아침 회의. HW팀 리더가 물었다. "펌웨어 테스트 상태는?" 나: "90% 정도요." HW팀 리더: "90%면 앞으로 7일 동안 마무리 되겠네요?" 아. 이미 계산이 되어 있었구나. 남은 10%는 뭘까. 아무도 모른다. 나도 모른다. 그래서 무섭다. 엣지 케이스의 악몽 어제 테스트 케이스 리스트를 다시 봤다. 문서는 총 47페이지. 펌웨어팀이 만든 게 아니라 QA팀이 만들었다. 아, QA팀은 테스트만 한다. 우리는 이 테스트를 통과하려고 코드를 짠다. 47페이지를 다 읽으면서 생각했다. '혹시 우리가 놓친 조건이 있나?' 예를 들면.온도 0도에서 켜졌을 때는 어떻게 되나? 전원을 뺐다가 5초 안에 다시 꽂으면? 시리얼 통신하는 도중에 전원 끊으면? 펌웨어 업데이트 중에 전원 끊으면?이런 것들. 생각하면 끝이 없다. 한 가지 놓친 게 있으면 리콜이다. 리콜 비용. CEO한테서 들은 소문은 '최소 3억'. 최악은 '10억 이상'. 우리 팀 연봉 다 더해도 그 정도다. 나 혼자면 20년을 일해도 못 버는 돈이다. 그럼 그만큼의 책임이 내 어깨에 있다는 뜻이다. 어제 저녁 10시. 새로운 엣지 케이스를 발견했다. 극저온에서 배터리가 들어갔을 때, 부트 시퀀스에서 전압 체크하는 타이밍이 밀릴 수 있다는 걸 알았다. 딱 1ms 차이. 1ms가 뭐 하는 건가 싶겠지만, 그 1ms 때문에 다른 인터럽트가 선점되고, 그럼 UART 버퍼가 터질 수 있다. 터지면 모니터링 명령이 날아가고, 장비가 시작되지 않는다. 회로도를 다시 봤다. 콘덴서 용량이... 아니다. 그건 HW팀 문제다. 그럼 우리가 할 수 있는 건? 타이머 할당을 바꾸거나, 인터럽트 우선순위를 조정하거나, 또는... 밤 12시. 코드를 고쳤다. 테스트를 돌렸다. 통과. 다시 돌렸다. 통과. 5번 더. 다 통과. 그런데 혹시 다른 케이스에서 망가진 건 아닐까? 회귀 테스트. 모든 테스트를 다시 돌린다. 3시간 30분. 다 통과. 아침 3시 30분. 잠에 든다.양산 후 되돌릴 수 없다 웹개발자 친구 있다. SI 회사 다닌다. "버그 있으면 그냥 배포 다시 하면 돼. 새 버전 올리고. 끝." 나: "..." "너는 왜 그렇게 신경 써?" 펌웨어는 롤백이 없다. 물론 기술적으로 불가능한 건 아니다. 펌웨어 업데이트 메커니즘이 있고, 장비를 수거해서 새 버전을 올리고 반송할 수 있다. 비용만 우리 회사에서 감당하면. 하지만 고객입장에서는? "이 제품 버그 있어서 교환해야 해요." 신뢰도가 떨어진다. 두 번 이상 리콜되면 우리 제품은 끝이다. 시장에서. 그래서 처음부터 완벽해야 한다. 완벽이라는 건 뭘까. 혼자서 생각하면 미친다. 여자친구가 있을 때. 그때도 이랬다. 야근 때문에 헤어졌다는 게 공식 이유지만, 사실은 이것 때문이었다. 내 정신이 여기 없었다. 펌웨어에. 양산에. 리콜의 악몽에. "넌 왜 항상 일에만 생각해?" "양산까지만. 양산 나가면..." 양산이 나갔다. 그 다음 날부터 다음 프로젝트의 양산이 시작됐다. 지금 여기 회사 카페. 아침 8시. 검은색 커피. 세 잔째. 옆 테이블 신입. 인턴. 밝다. 웃는다. 최고 7시간을 자는 거 같다. 나를 봤을 때 이런 생각이 있겠지. '저 사람 왜 저렇게 피곤해 보이지?' 답을 줄 수 없다. 말로는 불가능하다. 침대에 누워도 잠이 오지 않는다. 눈을 감고 기다린다. 5분. 10분. 20분. 그럼 차라리 일한다. 집에 가는 길. 9시 40분. 퇴근이 이른 날이다. 내일 테스트 결과를 생각한다. 실패하는 건 아닐까. 지금까지 다 했는데 갑자기? 그럼 HW팀 보고를 어떻게 하지. 우리가 못 찾은 버그가 있었다고? 아, 그리고 스펙 문서. 다시 읽어야 한다. 혹시 우리가 놓친 요구사항이... 이미 3번 읽었는데. 4번째 읽을까.D-6 회사 도착. 9시 15분. 테스트 결과 확인. 모두 통과. 다시 확인. 모두 통과. 한 번 더. 모두 통과. 그런데 이게 이상하다. 이렇게 잘 될 리가 없다. "혹시 테스트 자체에 문제는 없나?" 팀원이랑 테스트 코드를 다시 본다. 1시간. 이상 없다. 그럼 정말 다 잘 된 건가. 아니다. 뭔가 놓친 게 있다. 분명히. 야근할까. 아니다. 일단 오늘은 자자. 한 번 자본다. 제대로. 침대에 든다. 8시간을 자자고 다짐한다. 5시간을 잤다. 깼다. 또 코드가 생각난다. 포기한다. 회사로 간다. 그냥 이런 거다 이게 펌웨어 개발자의 삶이다. 양산까지 이런 거다. 웹개발자처럼 칼퇴할 수 없다. 배포 버튼 누르고 집 가면 끝. 문제 생기면 롤백. 대면 해결. 우리는 다르다. 보드에 코드가 올라가면 끝. 그 다음부턴 고객 손에. 고객이 전원을 켜면 우리 코드가 돈다. 온도 영하 20도인 한겨울에도. 습도 95%인 욕실에서도. 시골 할머니 손에서도. 모든 상황을 예상할 수 없다. 그래서 잠을 못 자는 거다. D-7이라는 숫자는 사실 숫자가 아니라 카운트다운이다. 남은 시간. 소진되어 가는 기회. 돌이킬 수 없는 경계선. 그 선을 넘는 순간, 우리 펌웨어는 세상으로 나간다. 그리고 나는 계속 그걸 생각한다. 밤마다. 천장을 보면서.내일도 버틴다. 그 다음날도. D-6일 때도 D-1일 때도. 그리고 양산 나간 후에도.