しかもテストベクタとは言っても、内蔵しているCPUで動作するプログラムのアセンブラソースがおかしいっていうお話でして、結構話が面倒なのです。だから力業(ちからざわ)と申し上げているのでして・・・。
しかしまあ、数百万個出荷された製品シリーズのテストに製品シリーズ共通で使われていたテストベクタに紛れ込んでいたバグだったので、見つけたこっちが青くなりました。
エンドユーザーに渡るデータではないにしても、テストベクタは不良を外部に流出させないための砦ですから、ちゃんとコードレビューをしないといけませんね。
製品開発の概要と担当業務について
私は2005年頃、32bit RISC CPUにDRAMコントローラやタイマーなどの各種周辺機能、そしてMPEG4コーデックのアクセラレータおよびMPEG4コーデック専用SDRAMコントローラ等を1チップに搭載した特定顧客向けのカスタムLSIの開発に参画しました。自主開発品ですが、実質はとある家電メーカーのminiDVカメラ向けカスタムLSI。
その開発の中で、私は論理合成以降、レイアウト設計担当への論理設計データ払い出しまでの業務の実務とりまとめと、テストエンジニアの経験があったので、テストベクタのデータ変換と払い出しの実務、テストエンジニアと共同でのLSIテスターによる試作品評価、テストエンジニアと論理設計者の間に入っての各種調整を行っていました。
言ってみれば、論理合成以降のレイアウト、レイアウト検証、アートワーク、マスク作成、試作手配以外の設計業務全般です。これらレイアウト関連の業務、製品試作に必要なマスクデータの作成、マスク作成、そして試作工程への色々な指示など、さらにLSIテスターのオペレーションや特性データの評価は専門部署があり、それらの専門部署にやって頂いていました。
製品開発の方針めいたもの
そして、MPEG4アクセラレータとそれに直接接続する映像用SDRAMコントローラ部を自部門で開発、これをCPUの内部バスに接続し、テスト時にはCPU周りとMPEG4周りは、LSIの最上位階層からそれぞれ独立して検証できる全体構成となりました。こうすることによって、CPU周りの検証は導入した膨大なテストベクタや検証環境がそのまま使えるのです。
テストベクタは、時間とともにLSIの端子に入力される1(ハイレベル)と0(ローレベル)の入力データと、その入力データを使ってLSIが動作したときに時間とともにLSIが出力するはずの出力値(出力期待値、もしくは単に期待値と言います。確率・統計で使う期待値とは意味が違います)から作られるものです。
LSIの論理設計のときに、動作の検証のために作られますが、これが試作品の動作検証、特性評価、及び量産品の出荷選別に使われるという、重要な設計データです。LSIの動作が定義されているデータと言っても良いものです。
特性評価
これらの試験で設計通りの動作をしているか、電源電圧の範囲にどの程度余裕があるか?動作温度範囲にはどの程度余裕があるか?電気的特性や周波数特性が設計値を満足しているかなど、様々な評価を行うのです。
特に、LSIテスターによる評価では、しきい値電圧やMOSトランジスタのゲート寸法などの各種製造パラメータを意図的にばらつかせた、限度サンプルと呼ばれるサンプルを特性評価用に作って、これらの試作サンプルを使って動作保証温度範囲内で、各種電気的特性の測定に加えて、設計時の論理検証で使ったテストベクタが余裕を持って正しく動作することを確認します。
トラブル発生
他のテストと比べて、ある特定のテストベクタを使った特性だけが良くないという状況です。調べてみたら、それは、CPUと密接につながっているキャッシュメモリーの動作周波数マージンを測定するためのテストベクタでした。
キャッシュメモリーは、今回開発したLSIの中で、最も高速に動作する回路であり、この部分のスピードが出ないと、 LSI全体の最大動作周波数が規定値を満足できません。しかしながら、一足先に論理設計チームが実施した実装機評価では、特に動作周波数がに余裕がないという結果は出ていなかったので、おそらくLSIテスター上での問題だろうという状況でした。
色々調べてみたら・・・
そうなると、いよいよそれよりも上流の工程、テストベクタを作るために搭載CPUで動かすソフトウエアの内容を確認して、変なプログラムになっていないかどうか見てみるしかありません。
実は、論理設計チームはこのときすでに解散していまして、別のプロジェクトを始めるための準備を始めていた時期でした。おそらく、あとはテスター担当がいればことが足りる、というプロジェクトマネージャーの判断だったのでしょうね。
それに、導入した設計データの中身の話、しかもハードウエア寄りの話になると、RTL主体の設計が業務の中心だった論理設計チームに対応できたかどうかわかりません。
私に出来る自信は全くありませんでしたが、量産品の出荷が迫っている時期だし、他に頼れる人もいないので、私が自分でテスターのログやらテストベクタ、アセンブラソースを見比べながら解析していくしか無いわけでして、しかも量産工程へのプログラムや製造図面の払い出し期日が迫っていたので、精神的にかなり辛かった覚えがあります。
ここでもし不具合が見つかれば、この製品のみならず、このCPUプラットフォームを使用している製品全てに影響が及ぶことになります。このCPUプラットフォームを使用した製品は携帯電話、デジタルカメラなど様々なセットに搭載されており、それまでの生産数は把握して居ませんが数百万個のオーダーだった印象です。
何をしたいテストベクタなのかはわかった
記憶では、キャッシュメモリーブロックに入っていくクロックの先頭からパルスが出るまでの時間と、パルスの幅を調整するためのレジスタだったと記憶しています。
もっとも、キャッシュメモリーのテストのためのレジスタなんていうものは、一般向けのハードウエアマニュアルには記載があるはずがありませんから、設計データを頂いてきた部署にお願いして、内部資料を読ませて頂いて、そのレジスタのアドレスやら使い方を調べながらの解析となりました。
そしてついに導入したテストベクタにバグを発見
mova @(TESTREG,pc),R0
mov REGVAL,R1 ---> REGVALの前の#を付け忘れている!
mov.l R1,@R0
上の3行がアセンブラソースとして書かれていたものですが、2行目が明らかに記載ミスです。このCPUにはこのような記述形式(アドレッシングモード)は定義されていないのです。本来は下の3行でなければなりません。
mova @(TESTREG,pc),R0
mov #REGVAL,R1
mov.l R1,@R0
つまり、
1行目 R0レジスタに、テストレジスタの実行アドレス(メモリー上の定数テーブル)を格納
2行目 R1レジスタに、テストレジスタに書き込む数値(数値そのもの)を格納
3行目 テストレジスタにR1レジスタの内容を書き込み
という意図で書かれたプログラムと思われますが、#が付いている方(こうでなければならないほう)はR1レジスタに文字通りREGVALという数値が符号拡張されて格納、(REGVALは8ビットの数値)、というのに対して、#を付け忘れると、全く意味の異なるプログラムになってしまいます。なお、この記事を書くに当たって色々調べたら、x86アーキテクチャでは数値の頭に何も付けない書き方をするのが正しいようですね。
旧来のCISC CPUなどでは、#を付け忘れた書き方の場合は、指定された数値をメモリーアドレスと解釈して、そのメモリーアドレスに対してレジスタの中身を書き込んだり、レジスタにメモリーの中身を転送する動きを指示するプログラムになる場合があります。
しかし今回のCPUでは、この#が付かない書き方、一般ユーザーが見るプログラミングマニュアルにはこの書き方の定義はなくて、市販されているアセンブラではエラーになるはずの間違った書き方です。
そのようなアセンブラでどのようなアセンブル結果になったのか記憶が定かではないのですが、
テストレジスタに書こうとしていた数値とは全く異なる数値がR1レジスタに格納され、テストレジスタに書き込まれていたのです。
読んでいただくとお気づきと思いますが、バグの原因なんて、こんなつまらない話である場合がほとんどです。「これならどうにもこうにも防ぎようがないね。誰も責められないよね」なんていう高尚な話であることはまずありません。ただ、そんなつまらない話であっても、見つけるまでにはそれなりの手間と時間がかかるのです。
どうやって修正するか?
論理設計チームはすでに解散、リスタートするためにはあれこれ説明のための資料やらなにやらを作ったり、導入元の部署の担当者を呼んだりと、リスタートのための会議を開くためには時間も人手もかかりそうです。大企業なので、何をするにも人を動かすためには色々と仁義を切る必要があるのでした。
なにせ、他部署から導入したテストベクタにバグがあるって話なので、おそらく内部の人間にこれを説明するのが一番大変。導入元は中身がわかってるからおそらくすぐに理解してくれます。実際後日報告したらすぐに理解してくれましたよ。
結局こうやって直しました
しかし、今回の場合は、
- 設計データの再利用は社内のいろんな事情からあり得ないと考えられること
- テストベクタを最初から最後まで見渡したところ、CPUに走らせるプログラムの読み込み以外は、キャッシュメモリーの書き込み読み出し成功のフラグがベクタの最後に1本の端子に出てくるだけという構造で、手修正での修正が難しくないこと
ということが分かったので、テストベクタの1/0をこっそり手修正して対応することにしました。間違いは明かだし、色々と調整している時間なんかなかったんで。もちろん終わった後に上に報告はしましたよ。
つまり、クロックを入力してリセットを解除すると、アドレスバスから所定のアドレスを出力し、RDピンがLレベルになり、そのときにデータバスに乗っているデータをフェッチしてプログラムを内部に読み込んでプログラムを実行、という動作を繰り返します。
間違った命令が書かれたROMのアドレスがアドレスバスから出力される場所でデータバスに乗っている値が、まさに修正すべき値で、ここに正しいプログラムを乗せてあげれば良いことになります。
当該箇所はループの中ではないので、数万ステップ(ここでは1ステップは外から与えるクロックで1クロックと考えてください)あるテストベクタの中から、修正する場所はせいぜい2ステップ程度、データバスに乗っているデータだけなのですが、修正場所を探す作業はそれほど大変ではなくて、アドレスバスの出力するアドレスを見れば探せます。
CPUが間違った命令を読んでいる場所でアドレスバスが出力しているアドレス値は、アセンブルリストを見ればわかるので、テストベクタの中のアドレスバスの出力期待値を読み出して、処理が楽なようにアドレス値を16進数に変換し、そのアドレス値のあるステップ数を見つけるというスクリプトをperlで作り、場所を見つけました。あとは、その場所でのデータバスに印加される数値を手修正です。
さすがに目視で探そうとしたら一大事ですが、自分で勉強しておいたperlが役に立ちました。
なお、この方法は今回のようにRISC CPUのような固定命令長CPUでは有効な方法ですが、命令長可変のCPUでは同じ命令でも#のあるなしで命令長が変わる可能性があるので、よほど運が良くないと不可能と思います。また、そもそもよほどの事情がない限り、こんな修正の仕方はしてはいけません。トラブルの元です(笑)
こうしてテストベクタの修正を行った後、最大動作周波数の温度依存性が悪化していないことをテストオペレーターに確認してもらって、テストベクタの払い出しを終えて、無事に日程通り量産適用することができました。
後味の悪い後日談
実は、この修正のあと、最高動作周波数の特性は良くなることは良くなったのですが、大幅に改善したというわけではありませんでした。しかしながら、設計者の意図とは異なった、間違ったテストベクタだったことは事実ですので、私の担当製品でその間違いを見つけて、私の担当製品では正しいテストベクタを適用できて、自分としては大きな成果だったと思っています。
その後設計データの導入元部署にこの件の報告をしましたけれども、形式的な感謝の言葉はあったもののさわって欲しくないところにさわられたという空気を感じました。なにせ、このテストベクタは不良品の流出を防いでくれる性質のものではありませんし、他のテストベクタを使用して、最大動作周波数の特性は担保されているのでしょう。問題があったとすれば、意図せず厳しいテストを実施したことになっていて、量産の歩留まりを不必要に下げていた可能性があることぐらいでしょうか。
不良流出の可能性は、おそらく少なかったのでしょうね。
また、事後に論理設計チームの実務とりまとめ者に今回の一件を説明するメールを出してみましたが、理解してくれなかったようで、ちょっと寂しい思いをしました。
学生時代から8bit CPUの機械語プログラムで遊び始めて、就職したあともマイコン搭載LSIの開発や実装機評価、LSIテスターを使った機能評価、特性評価の経験を総動員して解決に当たることが出来た事案だったと思っています。
0 件のコメント:
コメントを投稿
注: コメントを投稿できるのは、このブログのメンバーだけです。