月別アーカイブ: 2012年2月

MicroBlaze MCSでUART送信割り込みを使う

送信割り込みを使うプログラムを作成しました。
送信要求があればバッファに書き込み、送信割り込みがあればバッファからレジスタに書き込みます。
送信データが有れば割り込みを許可し、バッファが空になれば割り込みを禁止します。
これが基本的な考え方ですが、割り込み禁止状態で最初のデータをバッファに書いて
割り込みを許可しても割り込みが発生しないので、最初の文字の場合のみレジスタに書き込みます。
ソースが長くなったので今まで書いて来た事を含めてファイルをダウンロード出来る様にしました。
ソース
上をクリックして頂ければダウンロード出来ると思います。
ISEプロジェクトフォルダで使用される事を前提にしているので
必要ならmk.bat,Makefile,xdm.batを修正してください。
mk.batを実行(ダブルクリック)すればFPGAにダウンロードするdownload.bitファイルまで作成されます。
xdm.batは。xdmを実行します。xdm.iniがあるのでその指定に従って接続します。
GPI1 DIP-SW
GPO1 LED
GPO2 7seg-LED(bit0=A…6=g,7=DP,8=1,9=10,10=100,11=1000 すべてHアクティブ)
GPO3 LCD
FIT1 1mSで割り込み
PIT1 割り込みを使い7seg-LEDのダイナミック点灯
ハードは、自作のボードに合わせているのでそのまま使える人はいないと思うので上記をみて作成してください。
パスは、ISE WebPackを標準でcドライブにインストールした場合の設定になっています。
プログラムは、私の環境で動作したもので参考にして頂けるよう公開しました。
どのように使われても良いですが、何も保証は出来ませし改変する事もあり得ます。

追記
使用しているtopモジュールです。

DWMのSP3EでMicroBlaze MCS



DWM誌付録のSP3Eのテスト用に作成した基板にMicroBlaze MCS を乗せて動かしてみました。
7セグLEDに、FITでカウントした時間をPIT割り込みでダイナミック点灯させています。
LCDの表示、UARTと同時に問題無く動いています。
LCDのデータと制御線を同じGPOに割り当てたので、
制御線のみを切り替えたいとに&=や|=が使えない(GPOなのでリード出来ない)ので
変数に格納して操作してGPOに書くようにしました。

追記
LCDの部分はDDT誌No.8の78K0Rを元の作成したので、
ビットフィールドを使っていたのを上記の様に変更しました。
ARMでは、IOアクセスのレジスタにセット、クリアがありビット操作簡単ですが
FPGA上のMicroBlaze MCSではスライスの使用量が増えるので採用するのは難しいでしょうね。

MicroBlaze MCS のPIT1(libcを使う)

2/2の追記にも書きましたがPIT1のカウント値の問題の確認を行いました。
追記に書いたのはXDMで読み込んで確認しましたが、
UARTが動くようになったのでプログラムで読み出して表示します。
printfはメモリの使用量が多くて組み込めませんが
xil_printfが用意されているのでそれを使います。
xil_printfで使う出力処理を追加します。
//文字出力
void outbyte(char c)
{
  while((*(volatile unsigned int *)(UART_STATUS)) & 0x08);
  *(volatile unsigned int *)(UART_TX) = (unsigned int)c;
}
これでxil_printfでUARTから出力出来ます。
led.cの無限ループ内に
 xil_printf(“%d:”,(*(volatile unsigned int *) (PIT1_COUNTER)));
 xil_printf(“int_num=%d\n”,int_num);
の2行を追加します。
int_numは更新されていきますがPIT1のカウント値は更新されません。

追記
xil_printfが定義されてるので
#include <stdio.h>
を追加してください。
追記2
printfのメモリサイズはXilinxアンサーを参照ください。
libcの入力系は
char inbyte(void){
}
を定義すれば使えます。割り込みでバッファに書き込みそこから取り出す処理にするのが良いと思います。
入力は必要を感じていないので確認していません。

プログラムのバグ

今まで乗せていたプログラムled.cにバグを見つけました。
ブログに書いた手順で行えば問題有りませんが、
コンパイル時に最適化を行うと正しく動作しません。
割り込み回数をカウントするint_numがmain内で不変と判断されるのが原因で
定義部分にvolatileを付けてコンパイルすれば解決します。

DWM付録のSP3基板で動かしてみようと確認していて、
メモリサイズが8Kしか確保できないので最適化オプションを-Osに指定して気づきました。
もともとgdbで動かす所まで書く予定だったので最初から-g -O0でコンパイルするように書いてきました。
これなら最適化されないので問題ないです。(-gはデバッグ情報の生成)
基本的な所のミスでご迷惑をおかけした人(いないかもしれませんが)ご免なさい。
SP3(XC3S50)でもled.cが問題無く動作しました。
Slices 705/768=91%
になりました。
UART+PIT+GPI(8bit)+GPO(8bit)+RAM(8Kbyte)の32bitCPUが100MHzで動いています。
クロックは20MHzをDCMで100MHzにしています。

もし他に誤りなど気づかれた方は、ブログのコメントかメールで教えて頂けると幸いです。

MicroBlaze MCS でUART受信割り込み

UARTの確認にXDMで送受信だけでは手抜きだと自分でも思ったので
プログラムを作りました。
割り込み確認プログラムにUART受信割り込みを追加してエコーバックするようにしました。
受信エラーと送信可能かどうかのチェックは行っていません。
以下プログラムです。インデントは全角空白に変換しています。
////////////////////////////////
#define UART_RX     0x80000000
#define UART_TX     0x80000004
#define UART_STATUS   0x80000008
//
#define GPIO1      0x80000010
//
#define PIT1_PRELOAD  0x80000040
#define PIT1_COUNTER  0x80000044
#define PIT1_CONTROL  0x80000048
//
#define IRQ_STATUS   0x80000030 //R Interrupt Status Register
#define IRQ_PENDING   0x80000034 //R Pending Interrupt Register
#define IRQ_ENABLE   0x80000038 //W Interrupt Enable Register
#define IRQ_ACK     0x8000003C //W Interrupt Acknowledge Register

//グローバル変数
volatile int int_num;  //割り込み回数 2/8:最適化する場合はvolatileが必要

//割り込み処理
void interrupt_handler()  __attribute__ ((interrupt_handler));
void interrupt_handler()  //rtid
{
  unsigned int reg;
  reg = *(volatile unsigned int *)(IRQ_STATUS);
  if(reg & 0x08){ //PIT割り込み
    *(volatile unsigned int *)(IRQ_ACK)=0x08;
    int_num++;
  }
  if(reg & 0x04){ //UART受信割り込み
    *(volatile unsigned int *)(IRQ_ACK)=0x04;
    reg = *(volatile unsigned int *)(UART_RX); //データ受信
    *(volatile unsigned int *)(UART_TX) = reg; //データ送信
  }
}
//割り込み許可
void microblaze_enable_interrupts()
{ 
  __asm__(
  ”mfs  r12, rmsr\n\t”   //Read the MSR register
  ”ori  r12, r12, 2\n\t”  //Set the interrupt enable bit
  ”mts  rmsr, r12\n\t”   //Save the MSR register
  );
}
//割り込み禁止
void microblaze_disable_interrupts()
{ 
  __asm__(
  ”mfs  r12, rmsr\n\t”   //Read the MSR register
  ”andi  r12, r12, ~2\n\t”  //Clear the interrupt enable bit
  ”mts  rmsr, r12\n\t”   //Save the MSR register
  );
}
int main()
{
  int i;

  int_num = 0;
  *(volatile unsigned int *)(PIT1_PRELOAD) = 100000000; // preset値
  *(volatile unsigned int *)(PIT1_CONTROL) = 0x3; // Timer Enable, Auto load
  *(volatile unsigned int *)(IRQ_ENABLE) = 0x08|0x04; //PIT1 & UART_RX enabled

  microblaze_enable_interrupts();

  while (1) {
    //*(volatile unsigned int *) (GPIO1) =0x55;
    for(i=0;i < 1000000;i++){}
    //*(volatile unsigned int *) (GPIO1) =0xaa;
    *(volatile unsigned int *) (GPIO1) =int_num;
    for(i=0;i < 1000000;i++){}
    //if(int_num>8){
    // microblaze_disable_interrupts();
    //}
  }
}
//////////////
追記
チェックを行っていないと書くと手抜きみたいですが
受信エラーは、有っても対処方法がなからで
送信可能かどうかは、最短で受信割り込みは1文字受信時間間隔で発生し
1文字送信時間と等しく(送受信同一速度なので)ほとんどの場合送信可能になっているからです。
受信割り込みでなく文字出力する場合は、UART_STATUSのTx Usedをみないと駄目です。
上記の様な送信間隔が保証されないからです。
追記2/16
受信エラーの記述が誤っています。
受信エラー発生時には受信割り込み(bit2)と別の要因で割り込み(bit0)が発生します。
従って受信エラー発生の処理はそちらで行う事になります。
ステータスレジスタを読み込んでエラー表示ビットをクリアすることになります。

MicroBlaze MCS でUART

確認していなかったUARTの動作を確認しました。
DWM誌の付録のSP3Eで確認しているのですが、基板上に実装してある発振器が48MHzで
MicroBlaze MCSの設定を 100MHzから変更していませんでした。
単純には、100MHzを書き換えれば良いのですが時間がかかるので
DCMで48MHz/12*25して100MHzにすることにしました、これでデフォルトの9600bpsでの通信になります。
それに合わせて、リセット信号にDCMのロック信号を加えました。
ロックしていない場合とSWを押している間はリセットにしました。
XDMでUARTのレジスタアクセスで送受信出来ることを確認できました。
割り込み処理にするには。IRQ_STATUSを読んで場合分けすれば良いです。

これで一通り確認できましたが(FITもトグルするのを確認しました。)何をしまようか?
前に書いたXDMのSTDIOですが、マニュアルを読むと
C_USE_UART
Enables the UART interface. All other UART as well as AXI and PLB parameters are don’t care
0
になっているので普通には無理なようです。

Makefile

bitファイル作成までを行うMakefileを書いてみました。
ipcore_dirと同じ場所にsrcフォルダを作成しています。
以下2行のbatファイルを作成
PATH=C:\Xilinx\13.4\ISE_DS\EDK\gnu\microblaze\nt\bin;C:\Xilinx\13.4\ISE_DS\EDK\gnuwin\bin
make
ここから
##############
SRC = led.c
LDSRC = linker_script.ld
CC = mb-gcc
CFLAGS = -mlittle-endian -O0 -g
LFLAGS = -Wl,-T -Wl,$(LDSRC)
##############
CMD = C:/Xilinx/13.4/ISE_DS/ISE/bin/nt/data2mem
# -bm
P1 = ../ipcore_dir/mb_bd.bmm
# -bt
P2 = ../top.bit
# -bd
P3 = led.elf
#tag
P4 = mb
# -o b
P5 = ../download.bit
##############
$(P5) : $(P1) $(P2) $(P3)
$(CMD) -bm $(P1) -bt $(P2) -bd $(P3) tag $(P4) -o b $(P5)
$(P3): $(SRC) $(LDSRC) Makefile
$(CC) $(CFLAGS) -o $(P3) $(SRC) $(LFLAGS)
##############
clean:
del $(P3)
ここまで
青字の先頭にはタブが必要です。
作成したbatファイルを実行すれば依存関係をみてbitファイルを作成します。
ダブルクリックで実行する場合はbatファイルの最後にpauseを入れれば結果の確認ができます。
リンクスクリプトのメモリサイズを正しく設定していればリンク時にメモリ不足はエラーになります。
メモリ使用サイズは
mb-size led.elf
で表示出来ます。
追記
LFLAGSの前に#を入れてコメントアウトするとデフォルトのリンクスクリプトが使用されます。
グローバル変数を大きくとりメモリサイズを指定した場合はエラーになる条件でも
正常にコンパイル出来てdata2memもエラーを出しません。
LFLAGSに-Wl,-Map=map.txtを追加するとmapファイルが作成されてメモリの使用状況が判ります。
リンクスクリプトでなくてリンカスクリプと表記するのが正しいのかな?
追記2
この後に書いたUARTの確認時にデフォルトのリンクスクリプトでも問題なく動く事を確認しました。
追記で書いたdata2memでエラーにならないケースも、そうなる事を期待した条件での確認なので
簡単な動作確認プログラムなら問題にならないことは承知で書いています。
当然ですがスタートアップルーチンで使用するアドレスが有るので対応したスクリプトが必要です。
後は使う人の考え方次第だと思います。
-mcpu=vX.YY.ZオプションでバージョンCPUのバージョン指定が可能ですが、(今のMCSなら8.20.b)
今回のled.cでは指定の有無で生成されるelfに差はありませんでした。
最適化を禁止している条件なので安全を考えれば指定する方が良いのかもしれません。
追記3
複数のソースファイルになる場合は、
SRC = led.c led2.c
でもOKですが、まとめてコンパイルされるので
SRC = led.o led2.o
にすると推論規則でソースから.oファイルが生成されそれぞれが別にコンパイルされリンクされます。
必要なら依存関係を明示的に記述します。またcleanでも.oの記述を追加します。

MicroBlaze MCS でIOBusを使う

そろそろネタ切れですが、今回はCoreGenでIOBusを有効にします。
MicroBlaze MCSを使う側で
wire [31:0] IO_Address;
wire [31:0] IO_Read_Data;
wire IO_Ready;
assign IO_Read_Data = 32’h12345678;
assign IO_Ready =IO_Address[31] & IO_Address[30];//1’b1;
インスタンス部分で
.IO_Addr_Strobe(), // output IO_Addr_Strobe
.IO_Read_Strobe(), // output IO_Read_Strobe
.IO_Write_Strobe(), // output IO_Write_Strobe
.IO_Address(IO_Address), // output [31 : 0] IO_Address
.IO_Byte_Enable(), // output [3 : 0] IO_Byte_Enable
.IO_Write_Data(), // output [31 : 0] IO_Write_Data
.IO_Read_Data(IO_Read_Data), // input [31 : 0] IO_Read_Data
.IO_Ready(IO_Ready), // input IO_Ready
を追加します。
FPGAに書き込み動作が変更前と変わらないことと
xdmで接続してmrd 0xc0000000で12345678が読み出せることが確認出来ました。

XMDをSTDIOとして使えるとデバッグに便利だと思うのですが、
MDMがMicroBlaze MCSのレジスタマップ上にないので無理なようです。

最初につまずいたINTERNAL_ERRORをのぞけば簡単に使えた印象です。
MicroBlazeを使ったのは、CPUバージョンが4から5になるころなのでそこからの間の変更は把握していません。
MicroBlaze MCSは、簡単に組み込むことが出来るのが良いと思いました。
用途によりますがUARTが1つだと制御したい対象とデバッグ用の出力で最低2つ欲しいことも有るように思います。
ソフトはレジスタアクセスのレベルから書く必要がありますが(ドライバが生成されない)これは好みの問題でしょう。
ハードの仕様を読むか、APIの仕様を読むかの差でしかプログラムを書く側からは違いがないので。
ターゲットのメモリが限られている場合につかわれるであろうMCSでは現状(APIがなくても)でも問題ないと思います。