HDLソースコードからオリジナルCPU「DL166」の内部動作を理解するオリジナルCPUでバイナリコード入門(2)(1/2 ページ)

オリジナル4ビットCPUを用いてバイナリコードを学ぶ本連載。第2回はオリジナルCPU「DL166」のHDLソースコードを基に内部動作について解説する。

» 2023年03月14日 07時00分 公開
[今岡通博MONOist]

はじめに

 前回は、学習用のオリジナルCPU「DL166」のインストラクションセットについて説明しました。今回は、そのDL166の内部動作を見ていきましょう。CPUの動作を中から理解したプログラマーこそが、CPUに優しいコードを書けると思います。

⇒連載「オリジナルCPUでバイナリコード入門」バックナンバー

「DL166」が最初に動作したAltera(現在のIntel)製のFPGA 「DL166」が最初に動作したAltera(現在のIntel)製のFPGA

「DL166」のHDLソースコード

 リスト1はVerilog-HDLという言語で書いたDL166のコードです。FPGAやデジタル集積回路の設計などに用いられる言語です。一般にHDL=hardware description languageと呼ばれる言語の一つで、日本語ではハードウェア記述言語といいます。Verilog-HDLの文法については特に解説しませんが、C言語などのプログラミング経験がある方であればおおよそ察しが付くと思います。なお、このHDLソースコードはGitHubから取得することもできます。

⇒GitHubに掲載した「DL166」のHDLソースコード

1 module cpu(input reset,input clk,input[3:0] btn,output[3:0]led,output[3:0]adr,input[7:0]dout);
2   wire[4:0]op=dout[7:3]; 
3   wire[2:0]sss=dout[2:0]; // Instruction set (op is operator,sss is operalnd)    
4   reg c_flag;
5   reg [3:0]regs[7:0];
6   assign led = ~regs[6];
7   assign adr = regs[7];
8   always @(posedge clk)
9    if(reset==0) {regs[0],regs[1],regs[2],regs[3],regs[4],regs[6],regs[7],c_flag}=0;
10    else begin
11       regs[5]=btn;
12        casez(op)
13 /* MOV */    5'b00zzz: regs[op[2:0]]=regs[sss];
14 /* ADD */    5'b01000: if (regs[0]+regs[sss] > 15) c_flag=1;else begin regs[0]=regs[0]+regs[sss];c_flag=0;end
15 /* OR  */    5'b01001: regs[0]=regs[0]|regs[sss];
16 /* AND */    5'b01010: regs[0]=regs[0]&regs[sss];
17 /* XOR */    5'b01011: regs[0]=regs[0]^regs[sss];
18 /* INC */    5'b01100: if (regs[sss]+1 > 15) c_flag=1;else begin regs[sss]=regs[sss]+1;c_flag=0;end
19 /* NOT */    5'b01101: regs[sss]=~regs[sss];
20 /*RROTATE*/    5'b01110: regs[sss]=regs[sss]>>1| (regs[sss]<<3 & 4'b1000);
21 /*LROTATE*/    5'b01111: regs[sss]=regs[sss]<<1| (regs[sss]>>3 & 4'b0001);
22 /* JNC */    5'b1000z: regs[7]= (c_flag)?regs[7]+1:{op[0],sss};
23 /* JMP */    5'b1001z: regs[7]= {op[0],sss};
24 /* MVI */    5'b1010z: regs[0]= {op[0],sss};
25       endcase        
26 /* PC++ */ if(op[4:1]!=4'b1000 && op[4:1]!=4'b1001) regs[7]=regs[7]+1;
27    end
28 endmodule
リスト1 「DL166」のHDLソースコード

 ここからはリスト1でどのような定義を行っているのかを、各行に分けて解説していきます。

1行目

1 module cpu(input reset,input clk,input[3:0] btn,output[3:0]led,output[3:0]adr,input[7:0]dout);

 1行目ではモジュール名と入出力を定義しています。モジュール名はcpuです。resetは外部からの信号でcpuにリセットをかけるものです。clkはcpuにクロック信号を与えるための入力信号です。

 input[3:0] btnは、バス表記で4ビット分の入力信号が定義されています。btnはDL166を実装したFPGA評価ボードに搭載されている4つのスイッチに接続されます。output[3:0]ledは、バス表記で4ビット分の出力信号を定義しています。ledはFPGA評価ボードの4個のLEDに接続されます。

 output[3:0]adrはバス表記で4ビットの出力信号を定義しています。adrはcpuに接続されるメモリのアドレスに接続されます。input[7:0]doutはバス表記で8ビットの入力信号を定義しています。doutは外部メモリの出力ポートに接続されます。

 cpuは外部メモリに対して adrで指定したアドレスのインストラクションをdoutを通じてcpuに取り込みます。外部メモリは同じFPGA内に実装されます。なお、外部メモリはリスト1には登場しません。これは別モジュールで定義されており、上位モジュールでcpuと接続されます。

2〜11行目

2   wire[4:0]op=dout[7:3]; 
3   wire[2:0]sss=dout[2:0]; // Instruction set (op is operator,sss is operalnd)    
4   reg c_flag;
5   reg [3:0]regs[7:0];
6   assign led = ~regs[6];
7   assign adr = regs[7];
8   always @(posedge clk)
9    if(reset==0) {regs[0],regs[1],regs[2],regs[3],regs[4],regs[6],regs[7],c_flag}=0;
10    else begin
11       regs[5]=btn;

 2行目と3行目は、外部メモリから取得した8ビットのdoutを5ビットと3ビットに分割してopとsssに再定義しています。この後にインストラクションセットのデコードに用いられます。

 4行目のc_flagはキャリーフラグで用いられます。

 5行目で4ビット長のレジスターを8個用意します。これはcpuの内部レジスターで、それぞれの用途などについては前回の記事を参照してください。

 6行目ではレジスターR6を出力ポートのledに接続しています。ビットを反転しているのはFPGA評価ボードのLEDが負論理で接続されているためです。

 7行目ではレジスターR7を出力ポートadrに接続します。

 8〜11行目は、外部入力のclkの立ち上がりで駆動されます。ただし外部入力のresetが0の時、8個の内部レジスターとキャリーフラグを0に戻します。それ以外の場合は11〜27行までのコードが実行されます。

 11行目で外部入力のbtnの値をレジスターR5に代入します。

       1|2 次のページへ

Copyright © ITmedia, Inc. All Rights Reserved.