きしだのはてな このページをアンテナに追加 RSSフィード

2013-04-07(日) FPGAのおすすめ教材

[][]FPGAのおすすめ教材 05:00 FPGAのおすすめ教材 - きしだのはてな を含むブックマーク

ここ最近のエントリを見て、モテたくてFPGAを触りたくなったという人もいるだろうと思います。

ただ、FPGAを触るといっても、どこから始めればいいのか、何を買えばいいかわからない人も多いのではないでしょうか。

そこで、FPGAを触るために用意するもので、おすすめのものをまとめてみます。下書きでずっと放置していたのですが、ばたばたと公開しておきます。

前提としては、今までハードウェアを触ったことのないソフトウェア技術者が、今後FPGAでデータ処理をしてモテるための準備をするということを想定してます。


「まず3万円用意します」


最初に、FPGAの評価ボードが必要です。

いろいろ安い評価ボードもありますが、ある程度最初からデバイスがついていないと楽しくないし、勉強もやりにくいです。また、その評価ボードを使ったテキストも必要になります。

そういう点で考えると、いまはDE0一択になると思います。

DE0-nanoは値段も安いしA/Dコンバータもついているし、かわいくていいんですが、出力としてはLEDが8個あるだけで、7セグLEDがないので数字が表示できないし、学習にはつらいです。


ソリトンウェーブから入手できます。拡張キットも買ってください。

http://solitonwave.shop-pro.jp/?pid=15835753

2015/7/28 追記

後継でDE0-CVというのが出てます

http://solitonwave.shop-pro.jp/?pid=90445204


なぜ拡張キットも必要かというと、シリアルポートの部品とLCD、D/A A/Dコンバータ、ブレッドボードなどがついているからです。

ハードに慣れた人なら、ブレッドボードなどは持っているだろうし、シリアルポートも部品やさんで買えると思いますが、不慣れだと何を買っていいかもわからないし、バラで買うと結構高いです。


目的がデータ処理だとしても、やはり、デバイス、それもLEDなどの物理デバイスではなくICなどのデジタルデバイスの制御ができるようになっておく必要があります。

そういったデバイス操作として簡単な順にあげると

LED→シリアル通信→VGA→DAコンバータ→ADコンバータ→PS/2LCDSDRAMDDR SDRAM

のようになります。(※個人の感想です)

このうちSDRAMまでが、DE0+拡張キットで試すことができます。

DDR SDRAM以降は速くなるほど難しくなるようで、これはもう基本学習の域を超えそうです。


あとは入門書2冊


まずはこれ

この本は、ちょっと高いし、わかってしまうとたいしたことが書いてないので、評価は低いようなのだけど、やはり初めてハードウェアをさわるのには、このくらいのレベルのことが書いてあるとありがたい。

あと、上記の拡張キットのPDFとつながってるので、進めやすいです。


つぎにこれ

FPGA ボードで学ぶ組込みシステム開発入門 ?Altera編?

FPGA ボードで学ぶ組込みシステム開発入門 ?Altera編?

ハードにすでに慣れてる人にはこちらからで大丈夫だと思います。

NIOS CPUとの接続バスのことや、PS2SDRAM制御、そしてLinuxを動かすというところまで書いてあって、なかなか読み応えあります。

ただ、サンプルが基本的にNIOS CPUからの制御を前提としているので、ちょっと大掛かりになっていて、やはり上記DE0本から入ったほうがわかりやすかったです。


さいごにひとこと。

「モテなくても泣かない」

2013-02-12(火) A/DコンバータをAvalonバスでCプログラムから読んで温度を計る

[][]A/DコンバータをAvalonバスでCプログラムから読んで温度を計る 13:18 A/DコンバータをAvalonバスでCプログラムから読んで温度を計る - きしだのはてな を含むブックマーク

AlteraのFPGA開発環境には、Nios IIというFPGA組み込み用のCPUライブラリ(FPGAの世界ではIPというらしい)がついていて、このプロセッサをベースとしてCのプログラムが動かせるようになってる。

そこにSDRAMコントローラのIPやLCDコントローラのIPなどを組み合わせていくと、一台のコンピュータになる。


そうすると、ハードウェア制御はHDLで書いて、手続きはCで書いてという使い分けができるようになって、実際に動くモノを作るには非常に生産性がよい。

まあ、そうなると、Cの部分は単なる組み込み開発になってそんなに目新しいものでもなくなるのだけど、HDLのコンパイル(FPGAの世界では合成というらしい)に比べてCのコンパイルは格段に早いし、なにより手続きが書きやすいので、自分で作った回路の動作確認などもやりやすくなる。


ただ、そのためにはNios IIプロセッサとやりとりするためのAvalonバスに接続できる回路を書いてあげる必要があって、その作法を知っておかないといけない。

まあ、バスに接続するといっても、モジュールの入出力をあわせてあげるだけなのだけど。それが単独で動かすよりは結構大変。


そこで、ちょうど手元に2ch A/Dコンバータがあるので、練習に、これを制御しつつCプログラムから読み出せるようにコンポーネントを作ってみた。(A/DコンバータはSPIというプロトコルで制御するのだけど、SPIコアもすでに用意されてるので実際はそっちが使える)

そしたら、こんな感じでコンポーネントとして接続する。

f:id:nowokay:20130212124932p:image

なんか、一度登録した自作コンポーネントを認識してくれてないのだけど・・・


Cプログラムの開発はEclipseベースのツールで行う。

#include <stdio.h>
#include "io.h"
#include "system.h"
int main()
{
  printf("Hello from Nios II!Yes!!\n");

  int airtmp;
  int varreg;
  airtmp = IORD(ADC2CH_0_BASE, 1);
  varreg = IORD(ADC2CH_0_BASE, 0);
  double c = (airtmp - 0x14) / 4.;
  printf("var:%x tmp:%x=%.2f\n", varreg, airtmp, c);

  FILE* lcd = fopen("/dev/lcd_0", "w");
  fprintf(lcd, "hello from nios!\nvar:%x tmp:%.2f", varreg, c);
  fclose(lcd);

  FILE* uart = fopen("/dev/uart_0", "w");
  fprintf(uart, "hello!\n\rit is %.2fC\n\r", c);
  fclose(uart);
}

IORDが、Avalonバスから値を読み込むマクロ。

普通にprintfすると、Eclipseの出力ウィンドウにメッセージが表示される。LCDディスプレイやUARTシリアル通信のコントローラも組み込んでいるので、printfで出力できてすばらしい!

今回はch0に可変抵抗、ch1に温度センサをつけていて、その値が表示される。温度センサの値は、14hを引いて4で割ると摂氏温度になるらしい。

f:id:nowokay:20130212121648j:image

見にくいけど、LCDEclipseの出力とターミナルにメッセージが表示されている。19℃らしい。


最初リセット信号が、onでリセット、offでリセット解除となっていると気付かなくて、ハマりまくった。

A/Dコンバータのクロックが1KHzで、50MHzのネイティブ側からすれば5万クロックあるので、単純にwaitrequestを解除すると同じデータを出力するときに次の読み込み要求が来てしまって、アドレス指定を無視した結果が返ってしまっていた。ネイティブの1クロックだけwaitrequestを解除させて対処。

readyフラグみたいなのを用意して、Cプログラムでウェイトさせる必要もあるかも。


参考にしたのはこの本で、DE0でFPGAを始めるなら必ず目をとおしたほうがいいと思う。

FPGA ボードで学ぶ組込みシステム開発入門 ?Altera編?

FPGA ボードで学ぶ組込みシステム開発入門 ?Altera編?

Linuxを動かす解説もあるんだけど、ビルドが通らなかったのであきらめた。


Avalonバスの資料はこういうのがあった。日本語版はないのかな?

Avalon Interface Specifications」

http://www.altera.com/literature/manual/mnl_avalon_spec.pdf


あと、すでに用意されてるコンポーネントIPの説明はこんなのがあった。

「Nios II プロセッサ・リファレンス・ハンドブック セクションII. ペリフェラル・サポート」

http://www.altera.co.jp/literature/hb/nios2/n2cpu_nii5v1_02_j.pdf


とりあえずソースはこんな感じになった。

※2013/2/13 追記 MCP3002を想定していますが、最上位ビットがとれなかったり、タイミングの問題があるのでこのままでは使えません。データ取得をposedgeで行う必要があります。クロックも1MHzくらいで動くのでここまで遅くする必要はありません。

module adc2ch(
    input clk,
    input reset,
    input address,
    input read,
    output [15:0] readdata,
    output reg waitrequest,
    output sclk,
    output scs,
    output reg sdi,
    input sdo
);
    
    
    //1KHzをつくる
    reg [14:0] cnt;
    reg clk1k;
    always @(posedge clk or posedge reset) begin
        if(reset) begin
            cnt <= 15'd0;
        end else if(cnt == 15'd24999) begin
            if(!clk1k && scnt == 5'd16) begin
                waitrequest <= 1'b0;
            end
            clk1k <= clk1k + 1'b1;
            cnt <= 15'd0;
        end else begin
            cnt <= cnt + 15'd1;
            waitrequest <= 1'b1;
        end
    end
    
    //状態カウンタ
    reg [4:0] scnt;
    always @(posedge clk1k or posedge reset) begin
        if(reset) begin
            scnt <= 5'd0;
        end else if(scnt == 5'd16) begin
            scnt <= 5'd0;
        end else if(scnt == 5'd0) begin
            if(read)
                scnt <= 5'd1;
        end else begin
            scnt <= scnt + 5'd1;
        end
    end
    
    
    //SPI処理
    reg [9:0] tmpdata;
    reg [9:0] result;
    reg cs;
    assign scs = ~cs;
    assign sclk = clk1k & cs;
    assign readdata = {6'd0, result};
    always @(negedge clk1k) begin
        case(scnt)
        5'd0: begin
            cs <= 1'b0;
        end
        5'd1: begin //start
            cs <= 1'b1;
            sdi <= 1'b1;
        end
        5'd2: begin //single
            sdi <= 1'b1;
        end
        5'd3: begin //ch
            sdi <= address;
        end
        5'd4: begin //msbf
            sdi <= 1'b1;
        end
        5'd5: begin
            tmpdata <= 10'd0;
        end
        5'd16: begin
            cs <= 1'b0;
            result <= tmpdata;
        end
        default: begin
            tmpdata <= {tmpdata[8:0], sdo};
        end
        endcase
    end
endmodule

2013-02-08(金) 8bit CPUの命令長設定

[][][]8bit CPUの命令長設定 15:12 8bit CPUの命令長設定 - きしだのはてな を含むブックマーク

今回のCPUでは、デコード時に命令長を確定させないといけないのだけど、その処理を書いてなかった。

DE0で8bit CPUのデコーダを動かす


で、すでに非常に大きなcase文ができあがってて、そこにちくちくと命令長を設定していくのは非常に面倒だしミスも多そうと思って放置していた。

そんなところで、別の文脈だけど、 natsutanさんからアドバイスもらって、ソフトウェアじゃないんだからひとつの分岐ですべての処理をすませる必要はないのだなと気付いた。

Z80作成日記をみて思ったことをつらつらと - ぱたヘネ


というわけで、命令ごとの命令長を整理してみる。

f:id:nowokay:20130208150535p:image

そうすると、ほとんどが1で、わずかに2・3のところがあることがわかる。

こうやって分けて整理したほうが簡単な記述にできるし、ソフトウェアの場合と違って分岐の回数が増えても単純に実行効率が悪くなるわけでもない。むしろレイテンシが少なくなるかもしれない。


で、実装してみて、LCDに命令長も表示するようにしてみた。

f:id:nowokay:20130208145829j:image

よく考えたら7セグが2ケタあまってるんで、わざわざLCDに表示することもなかったのだけど、やはりLCDにまとめていろいろ情報を表示できるのは便利。

LCDの制御もだいぶ慣れた。


今回追加したコードはこんな感じ

    always @(code) begin
        case(sect)
            2'b01,2'b10:
                op_len <= 1;
            2'b00:begin
                if(subop[2:0] == 6)begin
                    op_len <= 2;
                end else if(subop == 1)begin
                    op_len <= 3;
                end else begin
                    case(code)
                        8'h20,8'h30,8'h18,8'h28:
                            op_len <= 2;
                        8'h22,8'h32,8'h38,8'h2a,8'h3a:
                            op_len <= 3;
                        default:
                            op_len <= 1;
                    endcase
                end
            end
            2'b11:begin
                if(subop[2:0] == 6)begin
                    op_len <= 2;
                end else begin
                    case(code)
                        8'hc2,8'hd2,8'hc3,8'hca,8'hda:
                            op_len <= 3;
                        8'hd3,8'hdb:
                            op_len <= 2;
                        default:
                            op_len <= 1;
                    endcase
                end 
            end
        endcase
    end

2013-02-02(土) DE0で回転処理をやってみる

[][]DE0で回転処理をやってみる 22:04 DE0で回転処理をやってみる - きしだのはてな を含むブックマーク

FPGAでの計算処理をやってみたかったので、画面の回転処理をやってみました。

最初、小数点以下6bitでやったんだけど、ちょっとカクカクするので、小数点以下を7bitとってみたらだいぶスムーズに。けど、やっぱ8bit欲しいなー

D


とりあえず回転処理やってる部分のソースだけ。

module rotation(
	input clk,
	output [9:0]led,
	input [9:0] sw,
	output [3:0] vga_r,
	output [3:0] vga_g,
	output [3:0] vga_b,
	output vga_hs,
	output vga_vs,
	output lcd_rw,
	output lcd_rs,
	output lcd_en,
	output [7:0] lcd_data,
	output lcd_blon

);

	assign led = 10'd0;
	//assign hled2 = 8'hff;
	//assign hled3 = 8'hff;

	wire [9:0] disp_x;
	wire [9:0] disp_y;
	
	reg[7:0] rad = 8'd32;
	reg[23:0] rad_cnt;
	always @(posedge clk or negedge reset) begin
		if(!reset) begin
			rad <= 8'd0;
			rad_cnt <= 24'd0;
		end else if(rad_cnt == 24'd1899999) begin
			rad_cnt <= 24'd0;
			rad <= rad + 8'd1;
		end else
			rad_cnt <= rad_cnt + 24'd1;
	end
	
	wire [8:0] cos;
	wire [8:0] sin;
	sin8 ss (rad, sin);
	sin8 sc (rad + 8'd64, cos);
	wire [16:0] cosf = {(cos[8] ? 8'hff : 8'h00), cos};
	wire [16:0] sinf = {(sin[8] ? 8'hff : 8'h00), sin};
	
	reg [16:0] cenx = 10'd320;
	reg [16:0] ceny = 10'd256;
	wire [16:0] xf = disp_x;
	wire [16:0] yf = disp_y;
	wire [16:0] xcos = (cosf * xf);
	wire [16:0] xsin = (sinf * xf);
	wire [16:0] ycos = (cosf * yf);
	wire [16:0] ysin = (sinf * yf);
	wire [16:0] cxcos = (cosf * cenx);
	wire [16:0] cxsin = (sinf * cenx);
	wire [16:0] cycos = (cosf * ceny);
	wire [16:0] cysin = (sinf * ceny);
	wire [16:0] dx = xcos + ysin - cxcos - cysin + cenx;
	wire [16:0] dy = ycos - xsin + cxsin - cycos + ceny;
	wire [9:0] imgx = dx[16:7];
	wire [9:0] imgy = dy[16:7];
	
	wire [11:0] rgb = 
		(imgy < 10'h100) ? {imgy[7:4], imgy[7:4], 4'hf} :
			(imgx[5] ^ imgy[4]) ? 12'hfff : 12'hfcc;
	vga v(clk, vga_r, vga_g, vga_b, vga_hs, vga_vs, disp_x, disp_y, rgb);
	
endmodule

2013-01-30(水) DE0で8bit CPUのデコーダを動かす

[][][]DE0で8bit CPUのデコーダを動かす 11:41 DE0で8bit CPUのデコーダを動かす - きしだのはてな を含むブックマーク

日曜日にデコーダが動いてたのだけど、動画をとるのがめんどくてブログかいてなかった。

とりあえず、ここで書いた、デコード後の内部命令がLCDに表示される。

ぼくのかんがえたさいきょうのCPU 〜 仕様確定


D


はっきりいって、命令にバイト数をつかえてきれいなコード体系にできれば、こんな面倒なことをしなくていいんだけど、メモリが少ない前提なので、やっぱこういう部分でがんばらないといけない。

あと、8080/Z80の命令体系に従わない部分をつくるなら、もうめんどうなところは全部命令体系を変更したほうが実装が楽だし回路も小さくなったなーとか思ったり。


これを実装するときは、まずJavaでコードを書いて、それなりに動くことを確認してから、手作業ではあるけど機械的にVerilogのコードに置き換えてます。

手続き的な処理を書くときには、このように書いたほうがよさげ。

このJavaコードは、そのまま逆アセンブラやエミュレータのデコード部として使えます。ただ、エミュレータにするのであれば、デコード部と実行部をわける必要はないので、そのまま使うわけではないけど。


まだこのままでは使えないけど、デコーダ部のコードだけ、ぺろっと貼っておきます。

`define OP_STORE 4'd0
`define OP_LOAD 4'd1
`define OP_IN   4'd2
`define OP_OUT  4'd3
`define OP_MOVE 4'd4
`define OP_JR   4'd5
`define OP_RLA  4'd6
`define OP_RRA  4'd7
`define OP_ADD  4'd8
`define OP_SUB  4'd9
`define OP_AND  4'd10
`define OP_XOR  4'd11
`define OP_OR   4'd12
`define OP_MUL  4'd13
`define OP_DIV  4'd14
`define OP_MOD  4'd15

`define COND_NONE 3'd0
`define COND_NZ 3'd1
`define COND_Z 3'd2
`define COND_NC 3'd3
`define COND_C 3'd4

`define REG_BC 4'd0
`define REG_DE 4'd1
`define REG_HL 4'd2
`define REG_SP 4'd3
`define REG_AF 4'd4
`define REG_VW 4'd5
`define REG_PC 4'd6
`define REG_II 4'd7

`define REG_B 4'd0
`define REG_C 4'd1
`define REG_D 4'd2
`define REG_E 4'd3
`define REG_H 4'd4
`define REG_L 4'd5
`define REG_SPh 4'd6
`define REG_SPl 4'd7
`define REG_A 4'd8
`define REG_F 4'd9
`define REG_V 4'd10
`define REG_W 4'd11
`define REG_PCh 4'd12
`define REG_PCl 4'd13
`define REG_I 4'd14
`define REG_NONE 4'd15

`define TRUE 1'b1;
`define FALSE 1'b0;

module decoder(
    input [7:0] code,
    input [7:0] param,
    output reg[3:0] op_code
);
    //reg[3:0] op_code;
    reg op_dw;
    reg[1:0] op_byte;
    reg[2:0] op_cond;
    reg[15:0] op_param1;
    reg[15:0] op_param2;
    reg[3:0] op_outreg;
    
    reg[7:0] regs[15];
    wire [7:0] reg8_A = regs[`REG_A];
    wire [7:0] reg8_F = regs[`REG_F];
    wire [7:0] reg8_V = regs[`REG_V];
    wire [7:0] reg8_I = regs[`REG_I];
    wire [15:0] reg_BC = {regs[`REG_B], regs[`REG_C]};
    wire [15:0] reg_DE = {regs[`REG_D], regs[`REG_E]};
    wire [15:0] reg_HL = {regs[`REG_H], regs[`REG_L]};
    wire [15:0] reg_SP = {regs[`REG_SPh], regs[`REG_SPl]};
    wire [15:0] reg_AF = {regs[`REG_A], regs[`REG_F]};
    wire [15:0] reg_VW = {regs[`REG_V], regs[`REG_W]};
    wire [15:0] reg_PC = {regs[`REG_PCh], regs[`REG_PCl]};

    wire[1:0] sect = code[7:6];
    wire[2:0] src = code[2:0];
    wire[2:0] dest = code[5:3];
    wire[3:0] subop = code[3:0];
    wire[1:0] reg16 = code[5:4];
    wire[3:0] destReg = 
        (dest == `REG_SPh) ? `REG_V :
        (dest == `REG_SPl) ? `REG_A :
                            {1'b0, dest};
    wire[3:0] srcReg =
        (src == `REG_SPh) ? `REG_V :
        (src == `REG_SPl) ? `REG_A :
                            {1'b0, src};
    wire[7:0] reg8_src = regs[srcReg];
    wire[7:0] reg8_dest = regs[destReg];
    wire[7:0] reg8_reg16 = regs[reg16];
    wire[15:0] reg16_reg = {regs[{1'd0, reg16, 1'd0}], regs[{1'd0, reg16, 1'd1}]};

    always @(code) begin
        case(sect)
        2'b00: begin
            case(src)
            3'b000: begin
                case(dest)
                3'b000: begin
                    //NOP(内部的にはLD A,Aを行う)
                    op_param2 <= reg8_A;
                    op_outreg <= `REG_A;
                    op_dw <= `FALSE;
                    op_code <= `OP_MOVE;
                end
                3'b001: begin
                    //LD V,A
                    op_param2 <= reg8_A;
                    op_outreg <= `REG_V;
                    op_dw <= `FALSE;
                    op_code <= `OP_MOVE;
                end
                3'b010: begin
                    // HALT(内部的にはJR nn)
                    op_param1 <= reg_PC;
                    op_param2 <= 16'hffff;
                    op_code <= `OP_JR;
                    op_outreg <= `REG_PC;
                    op_dw <= `TRUE;
                end
                default: begin
                    // JR cond, e
                    op_cond <= dest - 4'b0011;
                    op_code <= `OP_JR;
                    op_param1 <= reg_PC;
                    op_param2 <= param;
                    op_outreg <= `REG_PC;
                    op_dw <= `TRUE;
                end
                endcase
            end
            4'b0001: begin
                // LD r, nn
                op_param2 <= param;
                op_code <= `OP_MOVE;
                op_outreg <= reg16;
                op_dw <= `TRUE;
            end
            4'b1001: begin
                // ADD HL, r
                op_param1 <= reg_HL;
                op_param2 <= reg16_reg;
                op_code <= `OP_ADD;
                op_outreg <= `REG_HL;
                op_dw <= `TRUE;
            end
            4'b0010: begin
                op_code <= `OP_STORE;
                case(reg16)
                2'b00: begin
                    //LD (BC), A;
                    op_param2 <= reg_BC;
                    op_param1 <= reg8_A;
                    op_outreg <= `REG_NONE;
                    op_dw <= `FALSE;
                end
                2'b01: begin
                    //LD (DE), A;
                    op_param2 <= reg_DE;
                    op_param1 <= reg8_A;
                    op_outreg <= `REG_NONE;
                    op_dw <= `FALSE;
                end
                2'b10: begin
                    //LD(nn), HL
                    op_param2 <= param;
                    op_param1 <= reg_HL;
                    op_dw <= `TRUE;
                    op_outreg <= `REG_NONE;
                end
                2'b11: begin
                    //LD(nn), A
                    op_param2 <= param;
                    op_param1 <= reg8_A;
                    op_dw <= `FALSE;
                    op_outreg <= `REG_NONE;
                end
                endcase
            end
            4'b1010: begin
                op_code <= `OP_LOAD;
                case(reg16)
                2'b00: begin
                    //LD A, (BC);
                    op_outreg <= `REG_A;
                    op_dw <= `FALSE;
                    op_param2 <= reg_BC;
                    op_code <= `OP_STORE;
                end
                2'b01: begin
                    //LD A, (DE);
                    op_outreg <= `REG_A;
                    op_dw <= `FALSE;
                    op_param2 <= reg_DE;
                    op_code <= `OP_STORE;
                end
                2'b10: begin
                    //LD HL, (nn)
                    op_outreg <= `REG_HL;
                    op_param2 <= param;
                    op_dw <= `TRUE;
                end
                2'b11: begin
                    //LD A, (nn)
                    op_outreg <= `REG_A;
                    op_dw <= `FALSE;
                    op_param2 <= param;
                end 
                endcase
            end
            4'b0011: begin
                //INC rr
                op_outreg <= reg16;
                op_param1 <= reg16_reg;
                op_param2 <= 1;
                op_code <= `OP_ADD;
                op_dw <= `TRUE;
            end
            4'b1011: begin
                //DEC rr
                op_outreg <= reg16;
                op_param1 <= reg16_reg;
                op_param2 <= 1;
                op_code <= `OP_SUB;
                op_dw <= `TRUE;
            end
            3'b100: begin
                //INC r
                op_param1 <= reg8_dest;
                op_outreg <= destReg;
                op_dw <= `FALSE;
                op_code <= `OP_ADD;
                op_param2 <= 1;
            end
            3'b101: begin
                //DEC r
                op_param1 <= reg8_dest;
                op_outreg <= destReg;
                op_dw <= `FALSE;
                op_code <= `OP_SUB;
                op_param2 <= 1;
            end
            3'b110: begin
                op_dw <= `FALSE;
                case(dest)
                `REG_SPh: begin
                    // LD (HL), n
                    op_outreg <= `REG_NONE;
                    op_param1 <= param;
                    op_param2 <= reg_HL;
                    op_code <= `OP_STORE;
                end
                default: begin
                    // LD r, n
                    op_param2 <= param;
                    op_outreg <= destReg;
                    op_code <= `OP_MOVE;
                end
                endcase
            end
            3'b111: begin
                op_dw <= `FALSE;
                case(dest)
                3'b000: begin
                    // RLCA
                    op_param1 <= reg8_A;
                    op_param2 <= reg8_F;
                    op_outreg <= `REG_A;
                    op_code <= `OP_RLA;
                end
                3'b001: begin
                    // RRCA
                    op_param1 <= reg8_A;
                    op_param2 <= reg8_F;
                    op_code <= `OP_RRA;
                    op_outreg <= `REG_A;
                end
                3'b010: begin
                    // RLA
                    op_param1 <= reg8_A;
                    op_param2 <= 0;
                    op_code <= `OP_RLA;
                    op_outreg <= `REG_A;
                end
                3'b011: begin
                    // RRA
                    op_param1 <= reg8_A;
                    op_param2 <= 0;
                    op_outreg <= `REG_A;
                    op_code <= `OP_RRA;
                end
                3'b100: begin
                    // LD A, (HL)
                    op_param2 <= reg_HL;
                    op_code <= `OP_LOAD;
                    op_outreg <= `REG_A;
                end
                3'b101: begin
                    // CPL
                    op_param1 <= reg8_A;
                    op_param2 <= 8'hff;
                    op_code <= `OP_XOR;
                    op_outreg <= `REG_A;
                end
                3'b110: begin
                    // SCF
                    op_param1 <= reg8_F;
                    op_param2 <= 1;
                    op_code <= `OP_OR;
                    op_outreg <= `REG_F;
                end
                3'b111: begin
                    // CCF
                    op_param1 <= reg8_F;
                    op_param2 <= 1;
                    op_code <= `OP_XOR;
                    op_outreg <= `REG_F;
                end
                endcase
            end
            endcase
        end
        2'b01: begin
            // LD r1, r2
            op_dw <= `FALSE;
            if(dest == `REG_SPh) begin
                // LD (HL), r
                op_code <= `OP_STORE;
                op_param2 <= reg_HL;
                op_param1 <= reg8_src;
                op_outreg <= `REG_NONE;
            end else if(src == `REG_SPh) begin
                if(dest == `REG_SPl) begin
                // LD A, V
                    op_code <= `OP_MOVE;
                    op_outreg <= `REG_A;
                    op_param2 <= reg8_V;
                end else begin
                    // LD r, (HL)
                    op_outreg <= destReg;
                    op_code <= `OP_LOAD;
                    op_param2 <= reg_HL;
                end 
            end else begin
                // LD r1, r2
                op_code <= `OP_MOVE;
                op_outreg <= destReg;
                op_param2 <= reg8_src;
            end 
        end
        2'b10: begin
            op_outreg <= (dest == 3'b111) ? `REG_NONE : `REG_A;
            op_dw <= `FALSE;
            op_param1 <= reg8_A;
            op_param2 <= reg8_src;
            case(dest)
            3'b000: begin
                // ADD
                op_code <= `OP_ADD;
                op_cond <= 0;
            end
            3'b001: begin
                // ADC
                op_code <= `OP_ADD;
                op_cond <= reg8_F[0];
            end
            3'b010: begin
                // SUB
                op_code <= `OP_SUB;
                op_cond <= 0;
            end
            3'b011: begin
                // SBC
                op_code <= `OP_SUB;
                op_cond <= reg8_F[0];
            end
            3'b100: begin
                // AND
                op_code <= `OP_AND;
            end
            3'b101: begin
                // XOR
                op_code <= `OP_XOR;
            end
            3'b110: begin
                // OR
                op_code <= `OP_OR;
            end
            3'b111: begin
                // CP
                op_code <= `OP_SUB;
            end 
            endcase
        end
        2'b11: begin
            case(subop)
            4'b0110, 4'b1110: begin
                //op A,n
                op_outreg <= (dest == 3'b111) ? `REG_NONE : `REG_A;
                op_dw <= `FALSE;
                case(dest)
                3'b000: begin
                    // ADD
                    op_code <= `OP_ADD;
                    op_cond <= 0;
                end
                3'b001: begin
                    // ADC
                    op_code <= `OP_ADD;
                    op_cond <= reg8_F[0];
                end
                3'b010: begin
                    // SUB
                    op_code <= `OP_SUB;
                    op_cond <= 0;
                end
                3'b011: begin
                    // SBC
                    op_code <= `OP_SUB;
                    op_cond <= reg8_F[0];
                end
                3'b100: begin
                    // AND
                    op_code <= `OP_AND;
                end
                3'b101: begin
                    // XOR
                    op_code <= `OP_XOR;
                end
                3'b110: begin
                    // OR
                    op_code <= `OP_OR;
                end
                3'b111: begin
                    // CP
                    op_code <= `OP_SUB;
                end 
                endcase
                op_param1 <= reg8_A;
                op_param2 <= param;
                
            end
            4'b0010, 4'b1010: begin
                op_dw <= `TRUE;
                if(!dest[2]) begin
                    // JP f, nn
                    op_param2 <= param;
                    op_outreg <= `REG_PC;
                    op_cond <= {1'b0, dest[1:0]} + 3'd1;
                    op_code <= `OP_MOVE;
                end else begin
                    case(dest)
                    3'b100: begin
                        //INCW HL
                        op_outreg <= `REG_HL;
                        op_param1 <= reg_HL;
                        op_param2 <= 2;
                        op_code <= `OP_ADD;
                    end
                    3'b101: begin
                        // OUT (BC), HL
                        op_code <= `OP_OUT;
                        op_param1 <= reg_HL;
                        op_param2 <= reg_BC;
                        op_outreg <= `REG_NONE;
                    end
                    3'b110: begin
                        //INCW SP
                        op_outreg <= `REG_SP;
                        op_param1 <= reg_SP;
                        op_param2 <= 2;
                        op_code <= `OP_ADD;
                    end
                    3'b111: begin
                        // IN HL, (BC)
                        op_code <= `OP_IN;
                        op_param2 <= reg_BC;
                        op_outreg <= `REG_HL;
                    end 
                    endcase
                end 
            end
            4'b0000: begin
                op_dw <= `TRUE;
                case(reg16)
                2'b00: begin
                    // LD (HL), BC
                    op_param1 <= reg_BC;
                    op_param2 <= reg_HL;
                    op_code <= `OP_STORE;
                    op_outreg <= `REG_NONE;
                end
                2'b01: begin
                    // LD (HL), DE
                    op_param1 <= reg_DE;
                    op_param2 <= reg_HL;
                    op_code <= `OP_STORE;
                    op_outreg <= `REG_NONE;
                end
                2'b10: begin
                    // LD BC, (HL)
                    op_param2 <= reg_HL;
                    op_outreg <= `REG_BC;
                    op_code <= `OP_LOAD;
                end
                2'b11: begin
                    // LD DE, (HL)
                    op_param2 <= reg_HL;
                    op_outreg <= `REG_DE;
                    op_code <= `OP_LOAD;
                end
                endcase
            end
            4'b0001: begin
                // LD (SP), rr
                op_code <= `OP_STORE;
                op_outreg <= `REG_NONE;
                op_param1 <= (reg16 == `REG_SP) ? reg_AF : reg16_reg;
                op_param2 <= reg_SP;
                op_dw <= `TRUE;
            end
            4'b0011: begin
                case(reg16)
                2'b00: begin
                    // JP nn
                    op_outreg <= `REG_PC;
                    op_param2 <= param;
                    op_code <= `OP_MOVE;
                    op_dw <= `TRUE;
                end
                2'b01: begin
                    // OUT (n), A
                    op_param1 <= reg8_A;
                    op_param2 <= param;
                    op_code <= `OP_OUT;
                    op_outreg <= `REG_NONE;
                    op_dw <= `FALSE;
                end
                2'b10: begin
                    // OUT (BC), A
                    op_param1 <= reg8_A;
                    op_param2 <= reg_BC;
                    op_code <= `OP_OUT;
                    op_outreg <= `REG_NONE;
                    op_dw <= `FALSE;
                end
                2'b11: begin
                    // DI
                    op_param2 <= 1;
                    op_outreg <= `REG_I;
                    op_dw <= `FALSE;
                    op_code <= `OP_MOVE;
                end
                endcase
            end
            4'b0100: begin
                op_dw <= `TRUE;
                case(reg16)
                2'b00: begin
                    // LD VW, (SP)
                    op_param2 <= reg_SP;
                    op_outreg <= `REG_VW;
                    op_code <= `OP_LOAD;
                end
                2'b01: begin
                    // LD (SP), VW
                    op_param1 <= reg_VW;
                    op_param2 <= reg_SP;
                    op_outreg <= `REG_VW;
                    op_code <= `OP_STORE;
                end
                2'b10: begin
                    // DECW HL
                    op_outreg <= `REG_HL;
                    op_param1 <= reg_HL;
                    op_param2 <= 2;
                    op_code <= `OP_SUB;
                end
                2'b11: begin
                    // DECW SP
                    op_outreg <= `REG_SP;
                    op_param1 <= reg_SP;
                    op_param2 <= 2;
                    op_code <= `OP_SUB;
                end
                endcase
            end
            4'b0101: begin
                // LD rr, (SP)
                op_code <= `OP_LOAD;
                op_dw <= `TRUE;
                op_outreg <= reg16 == `REG_SP ? `REG_AF : reg16;
                op_param2 <= reg_SP;
            end
            4'b0111: begin
                // MUL A, r
                op_param1 <= reg8_A;
                op_param2 <= reg8_reg16;
                op_outreg <= `REG_HL;
                op_dw <= `TRUE;
                op_code <= `OP_MUL;
            end
            4'b1000: begin
                op_dw <= `TRUE;
                case(reg16)
                2'b00: begin
                    // JP Z, (VW)
                    op_cond <= `COND_Z;
                    op_code <= `OP_MOVE;
                    op_outreg <= `REG_PC;
                    op_param2 <= reg_VW;
                end
                2'b01: begin
                    // JP C, (VW)
                    op_cond <= `COND_C;
                    op_code <= `OP_MOVE;
                    op_outreg <= `REG_PC;
                    op_param2 <= reg_VW;
                end
                2'b10: begin
                    // SUB HL, BC
                    op_code <= `OP_SUB;
                    op_param1 <= reg_HL;
                    op_param2 <= reg_BC;
                    op_outreg <= `REG_HL;
                end
                2'b11: begin
                    // SUB HL, DE
                    op_code <= `OP_SUB;
                    op_param1 <= reg_HL;
                    op_param2 <= reg_DE;
                    op_outreg <= `REG_HL;
                end 
                endcase
            end
            4'b1001: begin
                op_dw <= `TRUE;
                case(reg16)
                2'b00: begin
                    //JP (VW)
                    op_code <= `OP_MOVE;
                    op_param2 <= reg_VW;
                    op_outreg <= `REG_PC;
                end
                2'b01: begin
                    //MUL BC, DE
                    op_code <= `OP_MUL;
                    op_param1 <= reg_BC;
                    op_param2 <= reg_DE;
                    op_outreg <= `REG_HL;
                end
                2'b10: begin
                    //JP (HL)
                    op_code <= `OP_MOVE;
                    op_param2 <= reg_HL;
                    op_outreg <= `REG_PC;
                end
                2'b11: begin
                    //LD SP, HL
                    op_code <= `OP_MOVE;
                    op_param2 <= reg_HL;
                    op_outreg <= `REG_SP;
                end
                endcase
            end
            4'b1011: begin
                op_dw <= `FALSE;
                case(reg16)
                2'b00: begin
                    // LD V, (HL)
                    op_code <= `OP_LOAD;
                    op_param2 <= reg_HL;
                    op_outreg <= `REG_V;
                end
                2'b01: begin
                    // IN A, (n)
                    op_code <= `OP_IN;
                    op_outreg <= `REG_A;
                    op_param2 <= param;
                end
                2'b10: begin
                    // IN A, (BC)
                    op_code <= `OP_IN;
                    op_outreg <= `REG_A;
                    op_param2 <= reg_BC;
                end
                2'b11: begin
                    // EI
                    op_code <= `OP_MOVE;
                    op_param2 <= 0;
                    op_outreg <= `REG_I;
                end
                endcase
            end
            4'b1100: begin
                op_dw <= `TRUE;
                if(reg16 == 2'b11) begin
                    // DIV BC, DE
                    op_param1 <= reg_BC;
                    op_param2 <= reg_DE;
                    op_code <= `OP_DIV;
                    op_outreg <= `REG_HL;
                end else begin
                    // LD rr, VW
                    op_param2 <= reg_VW;
                    op_outreg <= reg16;
                    op_code <= `OP_MOVE;
                end 
            end
            4'b1101: begin
                op_dw <= `TRUE;
                if(reg16 == 2'b11) begin
                    // MOD BC, DE
                    op_param1 <= reg_BC;
                    op_param2 <= reg_DE;
                    op_code <= `OP_MOD;
                    op_outreg <= `REG_HL;
                end else begin
                    // LD VW, rr
                    op_param2 <= reg16_reg;
                    op_outreg <= `REG_VW;
                    op_code <= `OP_MOVE;
                end 
            end
            4'b1111: begin
                // DIV A, r
                op_dw <= `FALSE;
                op_param1 <= reg8_A;
                op_param2 <= reg8_reg16;
                op_outreg <= `REG_A;
                op_code <= `OP_DIV;
            end
            endcase
        end
        endcase
    end

endmodule