長々とステートマシンを記述するのは苦手で、処理の変更も簡単にできるよう、
ステートマシンの代わりに、ソフトプロセッサを使用することにした。
FPGAに複数積むので、小型であること。ソフトウェアの開発が簡単にできるよう、gcc対応であることを条件に、
OpenCoresで探したところ、Navre
AVR clone(正確にはNavré AVR clone)を見つけたので、
調べたことの備忘録としてこのページにまとめる。
Navre AVR cloneとは
- http://opencores.org/project,navreで公開されているAVRコンパチブルのソフトプロセッサ
- 記述言語はVerilog
- 周辺回路や割り込み、スリープは実装されていない
- 命令セットは、http://en.wikipedia.org/wiki/Atmel_AVR_instruction_setの
Instruction set inheritanceの項目のClassic Core up to 8K Program Spaceまで対応のようだ
- 無条件分岐のIJMP命令(アドレスを16bitで指定)は対応しているが、
JMP命令(アドレスを22bitで指定)は対応していないようなのでプログラムは64Kwordまでのようだ
- 本来のAVRでは、データ空間にレジスタ・IOもマッピングされてるが、
ソースファイルを見たところ、NavreAVRではデータ空間に他のものがマッピングされておらず別々のものとして使ってるみたい?- つまり、データ空間はアドレス幅そのままの64Kbyte全て使えるかも?
- IO空間は64byteしか無いけどIN・OUT命令では、IO空間のアドレス幅は6bitまでなので、それ以上に拡張することはできないかも?
- IO空間が足りなければデータ空間にマッピングすればいいか?(本家AVRはそうなってるらしい)
ソフトウェアの開発
AtmelStudioを入れても、実際使うのはコンパイルに必要な物だけなので、無駄が多い。
また、メモリの容量などは実際のAVRとは違うものになるため、自分でリンカスクリプトを書くことになる。
→ AVR Toolchainをインストールしてコマンドラインで開発したほうがよさそう
AVR Toolchainのインストール
公式サイトからDL&インストール
パスは自動で通してくれるので、すぐ使える
ソフト設計・コーディング
普通のAVRとだいたい同じだと思う
ビルド
avr-gcc -mmcu=avr2 -c hoge.c // コンパイル
avr-gcc -mmcu=avr2 -o hoge.elf hoge.o // リンク
avr-objdump -d hoge.elf > hoge.dump // 機械語とアセンブリのリスト出力
avr-objcopy -I elf32-avr -O ihex hoge.elf hoge.hex // ELFファイルをIntelHex形式に変換
ビルドしたプログラムが、メモリに載る容量か確認
その後、FPGA内蔵メモリブロックの初期値としてプログラムを読み込むために
IntelHexファイルを$readmemhシステムタスク用に変換
avr-objcopy -I ihex -O verilog hoge.hex hoge_memh.txt
cat hoge_memh.txt
$ cat hoge_memh.txt
@00000000
0C C0 13 C0 12 C0 11 C0 10 C0 0F C0 0E C0 0D C0
0C C0 0B C0 0A C0 09 C0 08 C0 11 24 1F BE CF E5
D2 E0 DE BF CD BF 02 D0 35 C0 EA CF CF 93 DF 93
...
objcopyの出力する初期化ファイルのフォーマットは、バイトごとに空白で区切られているため、
$readmemhでそのまま読むと、ROMの上位8ビットはゼロで、下位8ビットに1バイトごと格納されてしまう。
AVRは1word=16bitなので、2バイトごとつなげて改行(あるいは空白)を入れるようにしなければいけない。
さらに、AVRはリトルエンディアンなので、バイトごとに前後入れ替えて繋げなければならない。
ちょうど同じことをするスクリプトを公開している方がいらっしゃったため、それを使わせてもらった。
http://www.koka-in.org/~kensyu/handicraft/diary/20110427.htmlのbyte2xst.pl
perl byte2xst.pl 2048 < hoge_memh.txt > test_memh_conv.txt
cat test_memh_conv.txt
C00C
C013
C012
C011
C010
C00F
...
あとは、VerilogのROM用コードを以下のようにすればFPGAに回路がロードされた時、プログラムがブロックメモリに置かれる
参考:
Altera Recommended HDL Coding StylesのExample 14–26. Verilog HDL RAM with Initialized Contents と Example 14–27. Verilog HDL RAM Initialized with the readmemb Command
module ram_with_init #(
parameter depth = 11,
parameter width = 16
) (
output reg [width-1:0] q,
input wire [width-1:0] d, // ROMの場合不要
input wire [depth-1:0] a,
input wire we, // ROMの場合不要
input wire clk
);
reg [width-1:0] mem [0:(1<<depth)-1];
initial
begin
$readmemh("C:\projdir\hoge_memh.txt", mem);
end
always @ (posedge clk) begin
if (we) // ROMの場合不要
mem[a] = d; // ROMの場合不要
q = mem[a];
end
endmodule
$readmemhでのファイル指定は、フルパスでないとシミュレーション時にModelSimが初期化してくれない。
改善方法知ってる方がいたら教えてくださると助かります。
テクニック
変数を配置するアドレスを固定する
int *hoge_p = (int*)0x400;
#define hoge *hoge_p // 普通の変数と同じように扱えるように名前変更
hoge = 4;
とすることで解決した。
はじめは、ARMのドキュメントで見つけた
__attribute__((at(0x400)))
と属性を付けてやる方法がスマートで良さそうだと考えていたが、
avr-gccでは対応していないようだ。
そのため、上記のような方法で宣言することになった。
最終更新:2013年11月12日 15:05