この記事は、私の最初のオープンソースプロジェクト「repl」(以下のリポジトリへのリンク)に関するものです。このプロジェクトのアイデアは、マイクロプロセッサプログラマが、マイクロプロセッサ内のプログラムをそのインターフェイスを介してデバッグできるようにすることですが、デバッグはjtagインターフェイスを介したデバッグと大差ありません。プログラムの有益なデバッグによって、プログラムを停止し、ブレークポイントを設定し、レジスター、メモリーを表示することが可能でした。
最初に頭に浮かぶのは、2xアプリケーションを作成することです。スレッドの一方はデバッグインターフェイスを担当し、もう一方はユーザープログラムを担当します。スレッド間の切り替えはタイマーで実行され、各スレッドには独自のスタックがあります。デバッグインターフェイスを作成するために束を使用しないことにしました。それらは2つの異なる方法で使用する必要があります。または、ヒープを操作する場合は、常に1つのスレッドに切り替える必要があります。
命令デバッグを実装するための最初のアイデアは、タイマー割り込み間の時間を十分に短縮して、1つの命令しか実行できないようにすることでした。このオプションは、Atmega328pマイクロプロセッサでの理想的な動作を示しました。実際、Atmegaの割り込み間の最小時間は1プロセッサクロックであり、実行に必要なクロックサイクル数に関係なく、実行が開始されると、すべての命令が常に終了します。
残念ながら、stm32に切り替えたとき、Cortex-Mコアが途中で命令の実行を中断し、中断から戻ったときに再度実行を開始する可能性があるため、このオプションは機能しませんでした。それから、問題はないと判断しました。命令が実行されるまで、割り込み間のクロックサイクル数を増やすだけです。その後、別の問題が発生しました。一部の命令は、後続の命令のみで実行されるか、まったく実行されません。残念ながら、この問題は解決できないため、アプローチを根本的に変える。
私はこれらの指示をエミュレートすることにしました。一見すると、Cortex Mコアエミュレーターを作成するというアイデア、そしてマイクロコンピューターのメモリに収まるものでさえ、素晴らしいように見えます。しかし、すべての命令をエミュレートする必要はなく、プログラムカウンターに関連付けられている命令だけをエミュレートする必要があることに気付きましたが、それほど多くはありませんでした。残りはすべて、別のメモリ位置に移動してそこで実行できるので、1つの命令だけが実行されるように、その後にジャンプ命令「b」を追加します。
そして、ブレークポイントの順番が来ました。それらの実装のために、while(1);ロジックを実装する命令を使用することにしました。ブレークポイントを設定するアドレスにある命令を置き換えて、タイマーの中断を待ちます。例外をスローする命令のようなものの方が良いことは理解していますが、ユニバーサルバージョンを作りたかったのです。実行されると、この命令を元に戻します。マイクロプロセッサのRAMでプログラムを実行することをお勧めします。そうしないと、マイクロコントローラのフラッシュメモリが長持ちしません。しかし、この時点で、私はすでにstm32の命令エミュレーターの作成を終了しており、Atmega328用に同じものを作成して作成する理由を決定しました。これで、命令を元に戻す必要はなく、エミュレートできます。
ランタイムとこのすべての友達を作るために、私は最初に自分のgdbクライアントを書きたかった。残念ながら、ideを操作するための2つのインターフェイスをサポートしています。どのideを使用するかを決めるのは彼女次第です。それらの両方を実装する(最初は非常に単純に見え、2番目はそれほど単純に見えませんでした)、さらにソースをファームウェアと組み合わせる必要がありましたが、これはあまり良い考えではないように思われました。そこで、自分でgdbserverを作成することにしました。幸い、プロトコルは1つしかなく、非常に単純でした。
私が実装することに決めた最初のインターフェースはGSMでした。トランシーバーとして、私はSIM800をサーバーとして、phpをサポートするサーバーとして使用することにしました。主なアイデアは、マイクロコンピューターからのポストリクエストの到着後、マイクロコンピューターへの接続を30秒間維持し、100ミリ秒ごとにデータベースに接続し、データが表示された場合は、リクエストへの応答として送信し、マイクロコンピューターからの次のリクエストを待つというものでした。
gdbクライアントのサーバーへの最初の接続は、pauseまたはstepコマンドを使用したgdbクライアントからサーバーへの要求が多すぎることを示していました。したがって、これらすべての要求をマイクロコンピューター用の1つの大きな要求に結合することにしました。このため、これらの要求のロジックを理解し、それらを予測する方法を学びました。これらのコマンドはそれほど速く実行されなかったので、もっと速くしたいのですが、耐えられます。
次のインターフェースはusbでした。Atmega328マイクロコンピューターでは、V-usbライブラリーを使用することにしました。 usbを操作するために、runコマンドロジックを書き直しました。これで、マイクロプロセッサは、このコマンドの後、一時停止コマンドを待ってプログラムを起動せず、1秒間起動してから、新しい実行コマンドが送信されました。このロジックが必要なのはプログラムの実行中にインターフェイスを無効にします。ドライバーを書く手間を省くために、標準のhidドライバーを使用することにしました。通信機能が機能レポートを取得するので、機能レポートを非表示にします。
マイクロシステムのフラッシュに関しては、これはusbインターフェースには不要であると判断したため、最初のファームウェアにはまだプログラマーが必要です。しかし、GSMインターフェースの場合はそれが最も多いです。通常、この目的のために別のプログラムを作成しますが、別の方法で作成することにしました。別のプログラムを作成する代わりに、プログラムをマイクロプロセッサのフラッシュメモリに完全にロードし、ダウンロードが完了したら、このプログラムをメモリの先頭にコピーすることにしました。それから、なぜプログラム全体を送信する必要があるのか、ファームウェアの現在のバイナリファイルと以前のバイナリファイルの違いしか送信できないのではないかと思いました。
この違いを最小限に抑えるために、ユーザープログラムの一部の.text、.data、.bss、.contructors配列セクションの名前を変更し(一般的な名前はマイクロコンピューターごとに異なります)、メインプログラムの直後のメモリに配置することにしました。
また、これらのセクションを初期化するために独自の関数を作成する必要がありました。現在、ほとんどの場合、小さなプログラムの変更は、転送される少量のデータに等しい小さなバイナリの変更に相当します。その結果、マイクロコンピュータは、RUN、STEP、PAUSEコマンドが機能するよりも速くフラッシュされることがよくあります。
そして最後に、作業のビデオ:
usbインターフェイスを介したStm32デバッグ。
gsmインターフェイスを介したStm32デバッグ。
usbインターフェイスを介したAtmega328デバッグ。
gsmインターフェイスを介したAtmega328デバッグ。
Gitリポジトリ