Cortex-M0+マイコンなSAMD21を使っているんだけど、
AtmelStudio開発環境で最初に同梱されているツールチェーンのバージョンが6.3.1だったりする。
古い(枯れた)環境で問題ないけども、最新のバージョンって気になるじゃん?ってことで入れてみた。
https://developer.arm.com/tools-and-software/open-source-software/gnu-toolchain/gnu-rm
上のURLから最新版をダウンロードしてきて、
「C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\arm\arm-gnu-toolchain」
この辺りのフォルダを差し替えるだけ!
まぁ、簡単で良かったけど、
gcc-arm-none-eabi-7-2018-q4はバグってるので
http://gcc-arm-none-eabi-7-2018-q2-update-win32.zip
1つ前をダウンロードしとく必要あり。
詳しくは下のサイトに載っています。
https://bugs.launchpad.net/gcc-arm-embedded/+bug/1810274
インターネット上にある断片化された情報を切り取って、リブログする。 主にソフトウェア、Ubuntu関連、CPUなど気になったニュース、また、日々の面白い出来事やニュースもリブログします。
2019年3月28日木曜日
2019年3月22日金曜日
【ARM】【Cortex-M0+】【SAMD21】DMA転送の罠
【DMA転送の罠】
ソフトバンクが買収したARM社のマイコンをこれから積極的に活用しようとして色々勉強してるんだけど、ARMコアのDMA転送のしくみは罠すぎるというか改善余地ありに思えるw
DMA転送を語る前にまずCPUの仕組みを理解するにはOSを設計できるレベルの知識が必要で、特にスケジューリング周りは外せない。
FreeRTOSなプリエンティブ、多段フィードバックキューやタイムスライスなど。
IT系の国家資格を持ってる人なら試験範囲の部分だし、自作OSの実装などでも触っている部分だしこの辺の事は大丈夫だろうと思って油断したのがARMコアのDMA転送。。
仕様上リングバッファ非対応だとか、ラウンドロビンで優先権を回す方式など、ある程度の仕組みを調べていざ動かしてみるとなぜかフリーズするんですよね。。
(海外サイトでも動かなくて解決案が無く、Githubでもコメントでここはそのうち・・みたいな感じになっていたり。PC向けARMって大丈夫?)
フリーズする理由でいくつか考えてみると、割込みが飛んでこないからっていう理由でコールバック処理されないなど色々模索してみたけど結果解決できず。。しかもレジスタを覗いてみるとラウンドロビンのFIFOキューは空っぽ、、静的な割り当てしても反応しない、、アクティブチャンネルを変えようと思っても切り替わらないしいったいどうなってるの状態。
さまざまな代替案やアプローチを考え試したけどうまくいかなくて、最終的にはPIOでカバーという非常手段を取っておきつつ、何度も1000ページ超える英文のドキュメントを読み直しては間違った解釈が無いか、抜け漏れがないか調べてようやく気付いた事がある。
ラウンドロビン・・・(; ・`д・´)?
あー・・・(悲しみ
通信周りに関して上位レイヤーから物理レイヤーまですべて精通してるつもりだったけど、そうでもなかった件について。。
ラウンドロビンって順番に優先権を渡して終わったら次!っていう感じに回していく仕組みだから、受信側のDMA転送は指定バイト数受信が終わるまで優先権を奪ってしまうんですよね。
つまり、タイムアウトなんて機能がないため
他のDMA転送を開始しない限り優先権は切り替わる事はなく、そのままフリーズする現象が起こる。
というのもバイト単位をビートという名称で括っていて、1ビート単位で受信したよ!って割込みを発生させることができれば解決するように思えるがうまくいかない。
割込みをやめてライトバックキャッシュをポーリングして自分で転送データ数をカウントしていこうと思っても割込みが発生しない=ライトバックキャッシュは更新されない。って現象。
では、参照するたびにサスペンド・リジュームしていけば割込み飛んでくるし解決?って思いきや、割込み入るまで16クロック程度かかったりするのでオーバーランのリスクが高まる。
以上の事から1ビートを1バイト単位に刻めば解決?って思うけどそれはそれでメモリ圧迫するからしたくないジレンマ。。
今日考えたのは、タイマー割込みをDMA転送にして割込み優先度を受信DMA転送より高くしてあげればいいんじゃない?って発想。
1ms割込みを無駄に消費するけど、バス効率が悪くなるだけでCPUの負担は少ないはず。。
これでもう一度DMA転送を試してみようと思う。
教訓:知っているからって油断するとハマる罠あり
ソフトバンクが買収したARM社のマイコンをこれから積極的に活用しようとして色々勉強してるんだけど、ARMコアのDMA転送のしくみは罠すぎるというか改善余地ありに思えるw
DMA転送を語る前にまずCPUの仕組みを理解するにはOSを設計できるレベルの知識が必要で、特にスケジューリング周りは外せない。
FreeRTOSなプリエンティブ、多段フィードバックキューやタイムスライスなど。
IT系の国家資格を持ってる人なら試験範囲の部分だし、自作OSの実装などでも触っている部分だしこの辺の事は大丈夫だろうと思って油断したのがARMコアのDMA転送。。
仕様上リングバッファ非対応だとか、ラウンドロビンで優先権を回す方式など、ある程度の仕組みを調べていざ動かしてみるとなぜかフリーズするんですよね。。
(海外サイトでも動かなくて解決案が無く、Githubでもコメントでここはそのうち・・みたいな感じになっていたり。PC向けARMって大丈夫?)
フリーズする理由でいくつか考えてみると、割込みが飛んでこないからっていう理由でコールバック処理されないなど色々模索してみたけど結果解決できず。。しかもレジスタを覗いてみるとラウンドロビンのFIFOキューは空っぽ、、静的な割り当てしても反応しない、、アクティブチャンネルを変えようと思っても切り替わらないしいったいどうなってるの状態。
さまざまな代替案やアプローチを考え試したけどうまくいかなくて、最終的にはPIOでカバーという非常手段を取っておきつつ、何度も1000ページ超える英文のドキュメントを読み直しては間違った解釈が無いか、抜け漏れがないか調べてようやく気付いた事がある。
ラウンドロビン・・・(; ・`д・´)?
あー・・・(悲しみ
通信周りに関して上位レイヤーから物理レイヤーまですべて精通してるつもりだったけど、そうでもなかった件について。。
ラウンドロビンって順番に優先権を渡して終わったら次!っていう感じに回していく仕組みだから、受信側のDMA転送は指定バイト数受信が終わるまで優先権を奪ってしまうんですよね。
つまり、タイムアウトなんて機能がないため
他のDMA転送を開始しない限り優先権は切り替わる事はなく、そのままフリーズする現象が起こる。
というのもバイト単位をビートという名称で括っていて、1ビート単位で受信したよ!って割込みを発生させることができれば解決するように思えるがうまくいかない。
割込みをやめてライトバックキャッシュをポーリングして自分で転送データ数をカウントしていこうと思っても割込みが発生しない=ライトバックキャッシュは更新されない。って現象。
では、参照するたびにサスペンド・リジュームしていけば割込み飛んでくるし解決?って思いきや、割込み入るまで16クロック程度かかったりするのでオーバーランのリスクが高まる。
以上の事から1ビートを1バイト単位に刻めば解決?って思うけどそれはそれでメモリ圧迫するからしたくないジレンマ。。
今日考えたのは、タイマー割込みをDMA転送にして割込み優先度を受信DMA転送より高くしてあげればいいんじゃない?って発想。
1ms割込みを無駄に消費するけど、バス効率が悪くなるだけでCPUの負担は少ないはず。。
これでもう一度DMA転送を試してみようと思う。
教訓:知っているからって油断するとハマる罠あり
2019年3月17日日曜日
VSCodeでCortex-Mコアのビルド環境を整える
【VisualStudioCodeは便利】
当時viかemacs、どちらを使うか悩んでいた時に
emacs派生のxyzzyを1か月使ってみて色々分かってきたんだけど
VIのシンプルさと軽量である点に惹かれてメインはVIM、しぶしぶVSを使うって感じでした。
(VSにはリファクタリング、コード補間機能が優れていて不満はないけど、もっさり感だけは・・・)
ちなみに今回VSCodeを使い始めようと思ったきっかけは
今やってるマイコンのプログラムのデバッグ環境がVIでは構築できなかったため。
最初は何をすればよいかサッパリ分からない状態だったけど
使い慣れていくうちに、これは便利だな・・・って思えるところまできましたw
というのも、GCCビルドする時もccacheを使う設定にしてみたり
GDB起動する時のConfigurationも自分で変更できるし。
むしろ書かないと何も動かないけど、書けば動くってところに魅力がある。
ってことで、今日はビルド&GDBデバッガ実行が整った記念にSS撮影w
まだ細かいところ、エラー行にジャンプする設定とかまだだけど、デバッガが使えるから重宝しそうです。
・・・さて、設定の内容に移ろうと思います。
まずは.vscodeフォルダに3つファイルがあります。
・c_cpp_properties.json ・・・インクルード関係(参照ジャンプ用)
・launch.json ・・・デバッガ設定
・tasks.json ・・・ビルド設定
大まかに分けるとこんな感じです。
インクルード関係は、コード上波線~~が出てるところにカーソルを合わせると電球マークが出てくるからそれをクリックするだけでOK。
デバッガ設定はGDBを使う場合は、最初JTAGポート開いてデバイスにアタッチして、ロードして、break mainまでの一連のバッチを書きます。
ビルド環境は、Makefileのあるフォルダ設定をして、make clean、make allを実行できるようにします。
もしビルドの高速化でccacheを使う場合は、Makefileに書かれている gcc部分の前にccacheを入れるだけでOKです。
例:
gcc -o hoge.c
これを
ccache gcc -o hoge.c
にするだけ!
コードを張り付けようと思ったけど、
まだビルドエラーした時のエラー行ジャンプがまだなので、次回にします。
VSCode上でCortex-M用のコードをビルド! |
当時viかemacs、どちらを使うか悩んでいた時に
emacs派生のxyzzyを1か月使ってみて色々分かってきたんだけど
VIのシンプルさと軽量である点に惹かれてメインはVIM、しぶしぶVSを使うって感じでした。
(VSにはリファクタリング、コード補間機能が優れていて不満はないけど、もっさり感だけは・・・)
ちなみに今回VSCodeを使い始めようと思ったきっかけは
今やってるマイコンのプログラムのデバッグ環境がVIでは構築できなかったため。
最初は何をすればよいかサッパリ分からない状態だったけど
使い慣れていくうちに、これは便利だな・・・って思えるところまできましたw
というのも、GCCビルドする時もccacheを使う設定にしてみたり
GDB起動する時のConfigurationも自分で変更できるし。
むしろ書かないと何も動かないけど、書けば動くってところに魅力がある。
ってことで、今日はビルド&GDBデバッガ実行が整った記念にSS撮影w
まだ細かいところ、エラー行にジャンプする設定とかまだだけど、デバッガが使えるから重宝しそうです。
・・・さて、設定の内容に移ろうと思います。
まずは.vscodeフォルダに3つファイルがあります。
・c_cpp_properties.json ・・・インクルード関係(参照ジャンプ用)
・launch.json ・・・デバッガ設定
・tasks.json ・・・ビルド設定
大まかに分けるとこんな感じです。
インクルード関係は、コード上波線~~が出てるところにカーソルを合わせると電球マークが出てくるからそれをクリックするだけでOK。
デバッガ設定はGDBを使う場合は、最初JTAGポート開いてデバイスにアタッチして、ロードして、break mainまでの一連のバッチを書きます。
ビルド環境は、Makefileのあるフォルダ設定をして、make clean、make allを実行できるようにします。
もしビルドの高速化でccacheを使う場合は、Makefileに書かれている gcc部分の前にccacheを入れるだけでOKです。
例:
gcc -o hoge.c
これを
ccache gcc -o hoge.c
にするだけ!
コードを張り付けようと思ったけど、
まだビルドエラーした時のエラー行ジャンプがまだなので、次回にします。
2019年3月2日土曜日
[C#]ビットアサインを表現する
久しぶりの更新です。
最近はマイコンのファームウェアを書いててC言語ばかりでC#も久々になってしまった。。
C言語よりC#のが便利!って思う所が沢山ある。
といってもC言語にはポインタがあって、こいつを利用しまくるとデータと型を組み合わせたりできてとても効率的なプログラムが書けるのは確かなんだけど、、
保守性を意識したり、文字列回りとかを意識するとやっぱ高級言語な方が便利だよね!って思う。
さて、通信プログラム界隈では受け取ったデータをビットフィールドで表現してアレコレやりたい!っていうのは叶うのんだけど、C#だとどうしても非効率なコードを書かないといけないのです。
もちろん誰かが作ったライブラリを使う手もあるのですが、コード生成系で出力されたコードはメンテナンスしたくない、、、みたいな好き嫌いがでてしまいますw
ということで自分で実装しました。
データ長はuint型(32bit)で固定してます。
long型にすれば64bit、
自在に操りたいならbyte型の配列にしてarray操作回りを書けばなんとか凌げるかな。
ーーー
もしテーブル使いたくなければ、算出する関数を使えばおk。
最近はマイコンのファームウェアを書いててC言語ばかりでC#も久々になってしまった。。
C言語よりC#のが便利!って思う所が沢山ある。
といってもC言語にはポインタがあって、こいつを利用しまくるとデータと型を組み合わせたりできてとても効率的なプログラムが書けるのは確かなんだけど、、
保守性を意識したり、文字列回りとかを意識するとやっぱ高級言語な方が便利だよね!って思う。
さて、通信プログラム界隈では受け取ったデータをビットフィールドで表現してアレコレやりたい!っていうのは叶うのんだけど、C#だとどうしても非効率なコードを書かないといけないのです。
もちろん誰かが作ったライブラリを使う手もあるのですが、コード生成系で出力されたコードはメンテナンスしたくない、、、みたいな好き嫌いがでてしまいますw
ということで自分で実装しました。
データ長はuint型(32bit)で固定してます。
long型にすれば64bit、
自在に操りたいならbyte型の配列にしてarray操作回りを書けばなんとか凌げるかな。
ーーー
using System; using System.Windows.Forms; namespace BitAssignCheck { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button8_Click(object sender, EventArgs e) { uint d = 0; int offset = 1; var v = d.GetInt(0, 3); for (var i = -7; i < 8; i++) { d = d.SetInt(i, offset, 3); var z = d.GetInt(offset, 3); if (z != i) { Console.WriteLine(z); } } } } public static class BitAssign { public static uint GetMask(int offset, int count) { var cnt = offset + count; var lo = (uint)((1 << offset) - 1); var hi = (32 == cnt) ? 0xffffffff : (uint)((1 << cnt) - 1); var result = hi - lo; return result; } static readonly uint[] mask = new uint[] { 0x00000001, 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff, 0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff, }; static readonly uint[] bits = new uint[] { 0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010, 0x00000020, 0x00000040, 0x00000080, 0x00000100, 0x00000200, 0x00000400, 0x00000800, 0x00001000, 0x00002000, 0x00004000, 0x00008000, 0x00010000, 0x00020000, 0x00040000, 0x00080000, 0x00100000, 0x00200000, 0x00400000, 0x00800000, 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, }; public static bool GetBool(this uint self, int offset) { var b = bits[offset]; var result = (0 != (self & b)); return result; } public static int GetInt(this uint self, int offset, int count) { var value = self >> offset; var b = bits[count]; var m = mask[count]; var sign = value & b; var data = value & m; var result = (0 == sign) ? (int)data : -((int)(data ^ m) + 1); return result; } public static uint GetUInt(this uint self, int offset, int count) { var value = self >> offset; var m = mask[count]; var data = value & m; return data; } public static uint SetBool(this uint self, bool value, int offset) { var b = bits[offset]; var m = 0xffffffff ^ b; var result = (value) ? (self | b) : (self & m); return result; } public static uint SetInt(this uint self, int value, int offset, int count) { return self.SetUInt((uint)value, offset, count); } public static uint SetUInt(this uint self, uint value, int offset, int count) { var mv = mask[count]; var data1 = (value & mv) << offset; var ms = 0xffffffff ^ (mv << offset); var data0 = (self & ms); var result = data0 | data1; return result; } } }
もしテーブル使いたくなければ、算出する関数を使えばおk。
public static uint GetMask(int offset, int count) { var cnt = offset + count; var lo = (uint)((1 << offset) - 1); var hi = (32 == cnt) ? 0xffffffff : (uint)((1 << cnt) - 1); var result = hi - lo; return result; } public static uint GetMask(int bits) { var cnt = bits + 1; var result = (32 == cnt) ? 0xffffffff : (uint)((1 << cnt) - 1); return result; } public static uint GetBits(int bits) { return 1 << bits; }
登録:
投稿 (Atom)