※上記の広告は60日以上更新のないWIKIに表示されています。更新することで広告が下部へ移動します。

LatticeMico8 v3.15チュートリアル


LatticeMico8とは


LatticeMico8Lattice Semiconductior社(以下Lattice)が公開している8bitソフトコアCPUです。オープンソースなのでLattice以外のFPGAでも使うこともできます。
同じようなFPGAメーカーが公開している8bitCPUにXilinxのPicoBlazeがありますが、あちらはアセンブラのみしか使えなませんが
LatticeMico8は開発ツールとしてGCCが提供されてるのでC言語が使えるのが強みです。
Latticeが公開している32bitソフトCPUコアにLatticeMico32がありますが、こちらは最小構成でも2000LUT以上リソースを消費します。
LatticeMico8の場合は250LUT程度のリソース消費量で済みますので8bit CPUでも十分な処理の場合はMico8の方が良いでしょう。

最新のバージョンは3.3ですが3.2以降は開発環境がLatticeMico32と同じMicoSystemBuilderに統合され
Eclipleベースのグラフィカルな環境で開発できますが、CPUアーキテクチャがMachXO2に最適化されてしまったのでそれ以外のデバイスでは使えません。
ここではLatticeXP2に組み込むことを目標に解説しますのでMicoSystemBuilderに統合される前の最後のバージョンであるバージョン3.15の解説をします。

このチュートリアルの目標

Diamond 1.4の環境でCQ-FRK-LXP2ボード上のLatticeXP2にLatticeMico8を組み込むことが目標です。
LatticeMico8のアーキテクチャの解説や詳しいツールの説明とかはすっ飛ばしてまくってますので、詳しいことはLatticeで公開されてるマニュアルを参照お願いします。
とりあえず開発環境をインストール。C言語のソースコードをコンパイル。CPUの出力信号をVeritakで確認する所まで解説します。

開発環境のインストール

まずLatticeMico8 Archive←のリンク先のページに移動します。
そこのリンク先からSDKとCPU本体のVerilogソースファイルをダウンロードします。
「LatticeMico8 Core Source Code Revision 3.15 Verilog Only」 ← CPU本体のVerilogソースファイルです
「NEW - LatticeMico8 Development Tools Installer for Windows - LatticeMico8 Core Revision 3.15」 ← SDKのインストーラです
上記のサイト内のリンクから2つのファイルをダウンロードしたら、インストーラを起動して適当なフォルダにインストールします。
この解説では「c:\lm8」というフォルダにインストールしたことにしておきます。
インストールする時にチェックボックスが出るのでCygwinをインストールするようにチェックを入れましょう。

テストコードのコンパイルとメモリファイルの作成


テストに使うプログラムのCソースコードを書きます。
// test.c
int main(){
	while(1){
		__builtin_export(0x00,0);
		__builtin_export(0xff,0);
	}
}
 
上記のプログラムでやってることは__builtin_export関数を使ってポート"ext_addr"に0を出力、"ext_dout"を0x00,0xff 交互に切り替えるだけです。

次にソースコードをコンパイルします。
LatticeMico8のツールのユーザーガイドにはmakeを使った方法が紹介されてますが、Makefileの書き方がわからないのと、
コマンドプロンプト起動するのは面倒くさいので、ソースコードのフォルダに下記の.batファイルを作ってそれでコンパイルします。
:: make.bat
del test.elf prom_init.mem scratchpad_init.mem
lm8-elf-gcc -Wall -Os -o test.elf test.c 
perl c:\lm8\scripts\lm8-deployer.pl c:\lm8\gtools\bin test.elf
pause
 
このバッチファイルを実行して"test.elf"、"prom_init.mem"、"scratchpad_init.mem"という3つのファイルが生成されたらコンパイル成功です。

シミュレーションの下準備


シミュレーションに使うテストベンチを示します
// tb_test.v
`timescale	10ns/10ps
 
module t;
 
	////////////////////////////////////////////////////
	//Parameters
	parameter sim_time = 500;
	////////////////////////////////////////////////////
	//Registers
	reg Clock = 1'b0;
	reg Reset = 1'b0;
 
	////////////////////////////////////////////////////
	//Net
 
	wire [7:0] ext_addr,ext_io_din,ext_dout,ext_mem_din;
	wire [4:0] addr_sp = ext_addr[4:0];
	assign ext_io_din = 8'b0;
 
	////////////////////////////////////////////////////
	//Instansiation
	isp8_core #(
		.FAMILY_NAME("XP2"),
		.PROM_FILE("prom_init.mem"),
		.PORT_AW(8),
		.EXT_AW(8),
		.PROM_AW(9),
		.PROM_AD(512),
		.REGISTERS_16(0),
		.PGM_STACK_AW(4),
		.PGM_STACK_AD(16))
	mico8(
		//input
		.clk(Clock),
		.rst_n(~Reset),
		.ext_mem_din(ext_mem_din),		//w:8
		.ext_mem_ready(1'b1),
		.ext_io_din(ext_io_din),		//w:8
		.intr(1'b0),
 
		//output
		.ext_addr(ext_addr),			//w:EXT_AW
		.ext_addr_cyc(ext_addr_cyc),
		.ext_dout(ext_dout),			//w:8
		.ext_mem_wr(ext_mem_wr),
		.ext_mem_rd(ext_mem_rd),
		.ext_io_wr(ext_io_wr),
		.ext_io_rd(ext_io_rd),
		.intr_ack(intr_ack)
	);
 
	pmi_distributed_spram #(
		.pmi_addr_depth(32),
		.pmi_addr_width(5),
		.pmi_data_width(8),
		.pmi_regmode("noreg"),
		.pmi_init_file("none"),
		.pmi_init_file_format("scratchpad_init.mem"),
		.pmi_family("XP2")
	) scratch_pad(
		.Address(addr_sp),
		.Data	(ext_dout),
		.Clock	(Clock),
		.ClockEn(1'b1),
		.WE		(ext_mem_wr),
		.Reset	(Reset),
		.Q		(ext_mem_din)
	);
 
	initial begin
		#0.5 Reset	= 1'b1;
		#0.5 Reset	= 1'b0;
		fork
			forever #0.5 Clock = ~Clock;
			do;
		join
	end
 
	task do; begin
		#sim_time;
		$finish;
	end	endtask
 
endmodule 
 

Veritakのプロジェクトファイルの作成
LatticeMico8_v3_15_Verilog.zipを解凍した後にできる下記のフォルダから上の画像に表示されているファイルを抜き出しておきましょう。
LatticeMico8_v3_15_Verilog\source\
LatticeMico8_v3_15_Verilog\models\pmi\
沢山ファイルが入ってますが実際使うのは上の画像で表示されてるファイルだけなので、
分けておくと後々論理合成するときプロジェクトにファイルを追加する際に余計な手間が省けます。
抜き出したファイルをプロジェクトに追加したら、PROMのメモリファイルを作成したフォルダをインクルードディレクトリに指定しておきましょう、
またはプロジェクトファイルと同じフォルダにメモリファイルをコピーしてしまってもOKです。

シミュレーション実行


上記のようにext_doutがext_addr_cycがHIになるたびffと00の変化を繰り返していれば成功です。

論理合成用のCソースコードの作成

CQ-FRK-LXP2上のLEDをチカチカさせるためのソースコードを作ります。いわゆるLチカです。
LatticeMico8がFPGA上で動作していることが確認できれば何でもいいのですが、やはりこれが一番簡単そうです。

論理合成用のCソースコードを示します。
// syn_test.c
int main(){
	unsigned char dout = 0;
	unsigned char i;
	for(;;){
		for(i=0;i<0xffff;i++){	// wait
			asm("nop");
		}	
		__builtin_export(dout,0);
		dout = ~dout;
	}
}
 
上記プログラムでやってることはfor文で時間稼ぎして、ext_doutをffと00を交互に繰り返すだけです。
要するにさっきのテストベンチでやったことをゆっくりやっているだけです。
for文の中にインラインアセンブラでnop命令を入れたのはGCCで最適化されてしまうのを避けるためです。

ファイルを作成したら先ほどののmake.batの記述を"test"の部分を"syn_test"変えてソースをコンパイルします。
ソースをコンパイルしたら論理合成用のトップモジュールファイルを作成します。
// top.v
module top(
	input Clock,
	output reg LEDOUT
	);
	wire Reset = 1'b0;
	wire [7:0] ext_addr,ext_io_din,ext_dout,ext_mem_din;
	wire [4:0] addr_sp = ext_addr[4:0];
	assign ext_io_din = 8'b0;
 
	////////////////////////////////////////////////////
	//Instansiation
	isp8_core #(
		.FAMILY_NAME("XP2"),
		.PROM_FILE("prom_init.mem"),
		.PORT_AW(8),
		.EXT_AW(8),
		.PROM_AW(9),
		.PROM_AD(512),
		.REGISTERS_16(0),
		.PGM_STACK_AW(4),
		.PGM_STACK_AD(16))
	mico8(
		//input
		.clk(Clock),
		.rst_n(~Reset),
		.ext_mem_din(ext_mem_din),	//w:8
		.ext_mem_ready(1'b1),
		.ext_io_din(ext_io_din),	//w:8
		.intr(1'b0),
		//output
		.ext_addr(ext_addr),		//w:EXT_AW
		.ext_addr_cyc(ext_addr_cyc),
		.ext_dout(ext_dout),		//w:8
		.ext_mem_wr(ext_mem_wr),
		.ext_mem_rd(ext_mem_rd),
		.ext_io_wr(ext_io_wr),
		.ext_io_rd(ext_io_rd),
		.intr_ack(intr_ack)
	);
 
	always@(posedge Clock or posedge Reset) begin
		if(Reset) begin
			LEDOUT	<= 1'b0;
		end else begin
			if(ext_addr_cyc)
				LEDOUT	<= ext_dout[0];
		end
	end
 
	pmi_distributed_spram #(
		.pmi_addr_depth(32),
		.pmi_addr_width(5),
		.pmi_data_width(8),
		.pmi_regmode("noreg"),
		.pmi_init_file("none"),
		.pmi_init_file_format("scratchpad_init.mem"),
		.pmi_family("XP2")
	) scratch_pad(
		.Address(addr_sp),
		.Data	(ext_dout),
		.Clock	(Clock),
		.ClockEn(1'b1),
		.WE		(ext_mem_wr),
		.Reset	(Reset),
		.Q		(ext_mem_din)
	); 
endmodule 
 
ext_addr_cycがHIの時、出力ポートレジスタLEDOUTにext_dout[0]を取り込むようにしています。

Diamondプロジェクトの作成

トップモジュールファイルを作成したら、Diamondを立ち上げてLFXP2-5E-5TN144C向けのプロジェクト新規作成します。
ここでは「LM8TEST」という名前で作成します。
次にFile ListのInput Filesに先ほどのトップモジュールファイルとシミュレーションで使ったLatticeMico8本体ファイル一式、
プリミティブモジュールをドラッグ&ドロップで追加します。

ファイルを追加したら、上のメニューの Project -> Set Top Level Unit で Set Top Level Unitの項目に「top」と入力して
module topをトップレベルユニットを設定します。
その後、プロジェクトファイル本体があるフォルダにプログラムファイルをコンパイル後にできた"prom_init.mem"と"scratchpad_init.mem"をコピーしておきます。
File ListのタブをProcessに切り替えてMap Designをクリックして、論理合成を行います。
論理合成が終了したらSpread Sheet Viewを開いてピンアサインと制約を追加します。

CQ-FRK-LXP2のDL3を点灯させますので LEDOUTに74、Clockに65を割り当てます。
出力レベルはLVCMOS33でPULLMODEはNONEに設定します。クロックピンに制約の追加も忘れずに。
FPGAに書き込んだ後目視でLEDがチカチカしていることが確認できたら成功です。
クロック速度10MHzぐらいを想定していますがそれ以上の場合は分周器CLKDIVBなどを使って下げると良いでしょう。

2012/01/18 update