Navre AVR clone

長々とステートマシンを記述するのは苦手で、処理の変更も簡単にできるよう、
ステートマシンの代わりに、ソフトプロセッサを使用することにした。
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とだいたい同じだと思う

ビルド

参考:http://www.clarestudio.org/elec/avr/gcc-1.html
ビルドする簡単な手順はおそらく以下のようになる。
対応している命令を指定するため、「-mmcu=avr2」オプションを追加している。
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形式に変換
ビルドしたプログラムが、メモリに載る容量か確認
avr-size hoge.elf
その後、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が初期化してくれない。
改善方法知ってる方がいたら教えてくださると助かります。

テクニック

変数を配置するアドレスを固定する

参照:http://stackoverflow.com/questions/4067811/how-to-place-a-variable-at-a-given-absolute-address-in-memory-with-gcc
FPGAに載せるプロセッサということで、複数のNavreAVRcloneを止めて一斉にRAMの中身を外部から読み書きするということを
しなければならない事があった。(本来はこんなトリッキーな手法を使うべきではないかもしれないが・・・)
そのため、読み書きする対象の変数のアドレスを固定しなければならない。
その方法は、
int *hoge_p = (int*)0x400;
#define hoge *hoge_p  // 普通の変数と同じように扱えるように名前変更
hoge = 4;
とすることで解決した。
はじめは、ARMのドキュメントで見つけた
__attribute__((at(0x400)))
と属性を付けてやる方法がスマートで良さそうだと考えていたが、
avr-gccでは対応していないようだ。
そのため、上記のような方法で宣言することになった。
最終更新:2013年11月12日 15:05