MieruEMB System V1.0 Verilog HDLのほとんどは,MieruPC2010 Version 1.4.0のコードを利用しています. MieruEMB System V1.0では,MieruPCのトップモジュールにあたるMieruPC.v をMieruEMB.v として名前を変更し, かつ内容に修正を加えています. ミニLCDのためのコントローラ minilcd.v が追加されています.また,I/Oに関するgpio.v に修正を加えています. これらを除いて,MieruPC2010 Version 1.4.0のコードを利用しています.
file name: define.v 1 /******************************************************************************************************/ 2 /* MieruPC2010 Version 1.4.0 ArchLab. TOKYO TECH */ 3 /******************************************************************************************************/ 4 5 /* Clock Interval Definition */ 6 /******************************************************************************************************/ 7 `define DCM_CLKFX_MULTIPLY 7 // you can modify this value, (40MHz / 8) * 7 = 35MHz 8 `define DCM_CLKFX_DIVIDE 8 // CLKFX_DIVIDE must be 1-32, CLKFX_MULTIPLY must be 2-32 9 10 /******************************************************************************************************/ 11 `define SYS_CLOCK 40 // 27MHz system clock 12 `define TIMER_CNT_WAIT (`SYS_CLOCK/`DCM_CLKFX_DIVIDE*`DCM_CLKFX_MULTIPLY*1000) // for 1kHz timer 13 `define CP0_CNT_WAIT (`SYS_CLOCK/`DCM_CLKFX_DIVIDE*`DCM_CLKFX_MULTIPLY*10) // for 100kHz CP0 timer 14 `define LCD_WAIT (`SYS_CLOCK/`DCM_CLKFX_DIVIDE*`DCM_CLKFX_MULTIPLY) // for LCD 1Mbps 15 16 /* Memory Controller / Initializer Constants */ 17 /******************************************************************************************************/ 18 `define ADDR 23:0 // Address Range 24bit 19 `define JADDR 21:0 // Address Range for J format (ADDR - 2bit, 26bit max.) 20 `define HALT_ADDR 24'h0000 // Branch Address to Halt 21 22 `define MMC_START_BLOCK 81 // MMC Image Start Physical Address: 0x0000a200 23 `define MMC_LAST_BLOCK 1104 // MMC Image End Physical Address: 0x00089fff |
define.vの最初の部分です. 7行目のDCM_CLKFX_MULTIPLYによって,システムの動作周波数を変更できます. 最初の設定では,35MHzで動作するようにしています. 例えば,45MHzに変更するためには,「`define DCM_CLKFX_MULTIPLY 9」とします. 50MHzに変更するためには,「`define DCM_CLKFX_MULTIPLY 10」とします. ただし,クロックを上げすぎると回路のタイミング制約を満たせないケースが生じるので, ISEのレポートを確認するようにしてください.
22行目と23行目のパラメータを変更することで,MMCカードから読み出すスタートブロックの番号と最後のブロック の番号を変更できます.標準は,81番目のブロックから512KBを読み出して, SRAMに格納する設定となっています.
file name: define.v 25 /* Instruction Processing Stage definition */ 26 /******************************************************************************************************/ 27 `define CPU_START 5'h00 28 `define CPU_IF0 5'h01 29 `define CPU_IF1 5'h02 30 `define CPU_IF2 5'h03 31 `define CPU_IF3 5'h04 32 `define CPU_IF4 5'h05 33 `define CPU_ID 5'h06 34 `define CPU_RF 5'h07 35 `define CPU_EX 5'h08 36 `define CPU_MA0 5'h09 37 `define CPU_MA1 5'h0a 38 `define CPU_MA2 5'h0b 39 `define CPU_MA3 5'h0c 40 `define CPU_MA4 5'h0d 41 `define CPU_WB 5'h0e 42 `define CPU_WCNT 5'h0f 43 `define CPU_HALT 5'h10 44 `define CPU_ERROR 5'h11 |
プロセッサMIPSCOREの命令処理のステージ定義が記述されています. この版に含まれるプロセッサは,(パイプライン処理をおこなわない)マルチサイクル版です.
ロード命令の場合には,IF0, IF1, IF2, IF3, IF4, ID, RF, EX, MA0, MA1, MA2, MA3, MA4, WB というステートを経由して, 1つの命令を処理するので,ロード命令の処理には14サイクルを必要とします. データメモリにアクセスしない加算命令では, IF0, IF1, IF2, IF3, IF4, ID, RF, EX, WB というステートを経由して,9サイクルを必要とします. このように,標準のプロセッサとしては(拡張や改良の余地のある)とても遅いものを採用しています.
file name: define.v 46 /* Instruction Attribute Definition */ 47 /******************************************************************************************************/ 48 `define READ_NONE 19'h00000 49 `define READ_RS 19'h00000 50 `define READ_RT 19'h00000 51 `define READ_RD 19'h00000 52 `define READ_HI 19'h00000 53 `define READ_LO 19'h00000 54 `define READ_HILO 19'h00000 55 `define WRITE_NONE 19'h00000 56 `define WRITE_RS 19'h00000 57 `define WRITE_RT 19'h00001 58 `define WRITE_RD 19'h00002 59 `define WRITE_HI 19'h00004 60 `define WRITE_LO 19'h00008 61 `define WRITE_HILO 19'h0000c 62 `define WRITE_RD_COND 19'h00010 63 `define WRITE_RRA 19'h00020 64 `define LOAD_1B 19'h00040 65 `define LOAD_2B 19'h00080 66 `define LOAD_4B_ALIGN 19'h00100 67 `define LOAD_4B_UNALIGN 19'h00200 68 `define LOAD_4B 19'h00300 69 `define LOAD_ANY 19'h003c0 70 `define STORE_1B 19'h00400 71 `define STORE_2B 19'h00800 72 `define STORE_4B_ALIGN 19'h01000 73 `define STORE_4B_UNALIGN 19'h02000 74 `define STORE_4B 19'h03000 75 `define STORE_ANY 19'h03c00 76 `define LDST 19'h03fc0 77 `define LOADSTORE_4B_UNALIGN 19'h02200 78 `define BRANCH 19'h04000 79 `define BRANCH_LIKELY 19'h08000 80 `define READ_CP0 19'h00000 81 `define WRITE_CP0 19'h10000 82 `define SYSTEM_CALL 19'h20000 83 `define BRANCH_ERET 19'h40000 84 85 /****************************************************************************************************** |
define.vの最後の部分には,命令の特徴示す定義が記述されています. これらの定義は,MipsCore.v で利用されます.
file name: MieruEMB.v 1 /******************************************************************************************************/ 2 /* MieruEMB System Version 1.0 since 2011-09 ArchLab. TOKYO TECH */ 3 /* See the README file on the parent directory for license information. */ 4 /******************************************************************************************************/ 5 /******************************************************************************************************/ 6 /* MieruPC2010 Version 1.4.0, 2011-10-05: Top Level Module ArchLab. TOKYO TECH */ 7 /******************************************************************************************************/ 8 `include "../rtl/define.v" 9 `default_nettype none |
多くのファイルでは,先にみた,define.vがインクルードされます(8行目). また,定義していないwireを利用するとエラーとなるように,`default_nettype none(9行目)を指定します.
file name: MieruEMB.v 11 /******************************************************************************************************/ 12 module MieruEMB(CLK, SW, LCD, ULED, GPIO, GPI, MMC_CS_X, MMC_DIN, MMC_SCLK, MMC_DOUT, 13 SRAM_A, SRAM_D, SRAM_OE_X, SRAM_WE_X, 14 LCD_CS0, LCD_CD, LCD_WR, LCD_RSTB, LCD_D); 15 input CLK, GPI; // input clock & general purpose input 16 input [2:0] SW; // input switch 17 input MMC_DOUT; // MMC Data Output 18 output LCD; // LCD Serial Output 19 output MMC_CS_X; // MMC Chip Select 20 output MMC_DIN; // MMC Data Input 21 output MMC_SCLK; // MMC Clock 22 output [2:0] ULED; // User LED 23 output reg [18:0] SRAM_A; // SRAM Address 24 output reg SRAM_OE_X; // SRAM Output Enable 25 output SRAM_WE_X; // SRAM Write Enable 26 inout [1:0] GPIO; // General Purpose I/O 27 inout [7:0] SRAM_D; // SRAM Data Bus 28 29 output LCD_CS0, LCD_CD, LCD_WR, LCD_RSTB; 30 output [7:0] LCD_D; 31 32 wire FCLK, RST_OX; // system clock and reset signal 33 wire WE, CORE_WE, MEM_WE, PLOAD_WE; // Write Enable signals 34 wire PLOAD_DONE; // Program load is done 35 wire [`ADDR] ADDR, PLOAD_ADDR, CORE_ADDR; // memory address 36 wire [7:0] DATA_IN, DATA_OUT, CORE_DATA, PLOAD_DATA; // data 37 38 CLKGEN clkgen(.CLK_I(CLK), .RST_X_I(SW[0] | SW[1] | SW[2]), .CLK_O(FCLK), .RST_X_O(RST_OX)); 39 40 MIPSCORE core(.CLK(FCLK), .RST_X(RST_OX & PLOAD_DONE), 41 .ADDR(CORE_ADDR), .DATA_IN(DATA_IN), .DATA_OUT(CORE_DATA), .WE(CORE_WE)); 42 43 PROGLOADER pload(.CLK(FCLK), .RST_X(RST_OX), 44 .DATA_IN(DATA_IN), 45 .ADDR(PLOAD_ADDR), .DATA_OUT(PLOAD_DATA), .WE(PLOAD_WE), .DONE(PLOAD_DONE)); 46 47 IOCON iocon(.CLK(FCLK), .RST_X(RST_OX), 48 .LCD(LCD), .GPIO(GPIO), .SW(SW), .GPI(GPI), 49 .ADDR(ADDR), .DATA_IN(DATA_IN), .DATA_OUT(DATA_OUT), 50 .WE(WE), .MEM_WE(MEM_WE), .MEM_Q(SRAM_D), 51 .MMC_CSX(MMC_CS_X), .MMC_DIN(MMC_DIN), .MMC_DOUT(MMC_DOUT), .MMC_SCLK(MMC_SCLK)); |
MieruEMB.vの前半です. 38行目のCLKGENは,Xilinx FPGAが持つDCM(デジタルクロックマネージャ)を用いて, define.v にて指定したクロック信号FCLKを生成します.デフォルトの設定では,これは35MHzです. MieruEMB.vではクロック信号としてFCLKのみを利用します. CLKGENを除いて,CLKを用いてはいけません. また,CLKGENはリセット信号 RST_OXを生成します.
MieruEMBシステムでは,3つのスイッチを同時に押すことでリセットとなります. スイッチが押されるとハードウェア的にはGNDレベルになるので, 3つのスイッチを同時に押すことで SW[0], SW[1], SW[2] が全て0になります. (SW[0] | SW[1] | SW[2]) を入力とすることで,全てのスイッチが押されているときだけ0となる入力信号 を生成しています.
40行,41行ではプロセッサであるMIPSCOREのモジュールを生成しています. SDカードからプログラムを読み出してSRAMにコピーするまでは,プロセッサは動作させません. このため,40行目で,(RST_OX & PLOAD_DONE) をリセットの入力としています. PLOAD_DONEはプログラムのコピーが終わるまでは0なので,この間はリセット信号が有効となります.
43行から45行では,SDカードからプログラムを読み出してSRAMにコピーするモジュールを生成しています. 47行から51行では,メモリマップドI/Oの処理などを含むI/Oコントローラのモジュールを生成しています.
file name: MieruEMB.v 52 53 reg lcd_reg; 54 always @(posedge FCLK) lcd_reg <= ~LCD; 55 56 assign ULED[0] = (!PLOAD_DONE) ? 1 : lcd_reg; 57 assign ADDR = (!PLOAD_DONE) ? PLOAD_ADDR : CORE_ADDR; 58 assign DATA_OUT = (!PLOAD_DONE) ? PLOAD_DATA : CORE_DATA; 59 assign WE = (!PLOAD_DONE) ? PLOAD_WE : CORE_WE; 60 61 reg [7:0] D_KEEP; 62 always @(posedge FCLK or negedge RST_OX) begin 63 if (!RST_OX) begin 64 SRAM_A <= 0; 65 D_KEEP <= 0; 66 SRAM_OE_X <= 0; 67 end else begin 68 SRAM_A <= ADDR[18:0]; 69 D_KEEP <= DATA_OUT; 70 SRAM_OE_X <= MEM_WE; 71 end 72 end 73 74 assign SRAM_D = (SRAM_OE_X) ? D_KEEP : 8'hzz; 75 assign SRAM_WE_X = ~FCLK | ~SRAM_OE_X; 76 77 reg [24:0] cnt_t; 78 always @(posedge FCLK) cnt_t <= cnt_t + 1; 79 assign ULED[2] = cnt_t[24]; 80 assign ULED[1] = (~SW[0]) | (~SW[1]) | (~SW[2]); 81 82 minilcd_con lcdcon (FCLK, (RST_OX & PLOAD_DONE), CORE_ADDR[13:0], CORE_DATA, 83 (CORE_WE & CORE_ADDR[23:16]==8'h90), 84 LCD_CS0, LCD_CD, LCD_RSTB, LCD_D, LCD_WR); 85 endmodule 86 87 /******************************************************************************************************/ |
MieruEMB.vの後半です. 53行と54行は,シリアルLCDの出力をレジスタlcd_regに格納しています. MieruEMBでは,シリアルLCDは利用しないので,lcd_reg の値は0となります. 56行目では,ULED[0]すなわち基板上のD2(発光ダイオード)の出力を記述しています. ULED[0]はプログラムロードが終わっていない場合に1としているので, プログラムロードの間のみ点灯します.
57行目から59行目ではSRAMにアクセスするための信号を選択しています. プログラムロードの間は,モジュールploadの信号を用い, そうでない場合にはプロセッサの信号(CORE_ADDRなど)を用います.
61行から75行では,SRAMへの信号を生成しています. SRAM_Dは入出力線なので,SRAMがデータを出している時には, FPGAからの出力をハイインピーダンス(z)にしないといけません.
77行から79行では,ULED[2]の信号を生成しています. これは,24ビットカウンタの最上位ビットを割り当てているので,基板上のD4(発光ダイオード)は一定間隔で 点滅します. 80行は,ULED[1]の記述です.スイッチのどれかが押された時にD3が光ります.
82行から84行はミニLCDのコントローラであるlcdconを生成しています. このモジュールはMieruEMBに特有のもので,minilcd.vに記述されています.
file name: minilcd.v 9 module minilcd_con(CLK, RST_X, VRAM_ADDR, VRAM_DATA, VRAM_WE, 10 LCD_CS0, LCD_CD, LCD_RSTB, LCD_D, LCD_WR); 11 input CLK, RST_X; 12 input [13:0] VRAM_ADDR; 13 input [7:0] VRAM_DATA; 14 input VRAM_WE; 15 output LCD_WR; 16 output reg LCD_CS0, LCD_CD, LCD_RSTB; 17 output reg [7:0] LCD_D; 18 19 reg init; 20 reg [14:0] cmdcnt; 21 reg [2:0] writecnt; 22 reg [23:0] waitcnt; 23 wire [15:0] dout; 24 wire [7:0] D; 25 26 assign LCD_WR = ~writecnt[2]; 27 28 minilcd_initmem mlcdmem(CLK, cmdcnt[5:0], dout); 29 30 minilcd_vram vram(.CLK(CLK), .DIN(VRAM_DATA[3:0]), .WADDR(VRAM_ADDR), .WE(VRAM_WE), 31 .DOUT(D), .RADDR(cmdcnt[14:1])); |
モジュール minilcd_conの前半です. minilcd_conは,FPGAの内部にビデオRAM (VRAM, 30行と31行)を持ちます. これは,4bit x (128x128 エントリ) で,8KBのメモリです. コントローラはこのVRAMの値を読み出しながら,適切なタイミングでミニLCDに表示していきます. 一方,12行から14行で宣言しているVRAM_ADDR, VRAM_DATA, VRAM_WEを用いて,VRAMに値を書き込みます. VRAMは読み出しと書き込みが干渉しないように,読み出しポートを1つ,書き込みポートを1つもつ デュアルポートメモリとして実現されます.
このモジュールを利用するためには, このモジュールの出力LCD_CS0, LCD_CD, LCD_RSTB, LCD_D, LCD_WRをトップモジュールの同じ信号に接続します. 次に,これを生成するモジュールの中から, VRAM_ADDR, VRAM_DATA, VRAM_WEを制御します. VRAMに色情報を書き込むと,書き込まれたアドレスのピクセルにその色が表示されるので, ピクセルのアドレスを VRAM_ADDR,色情報をVRAM_DATAとして,VRAM_WEを1とすることで, VRAMに書き込まれます.1クロックで1ピクセルの色データを書き込むことが可能です.
モジュールminilcd_initmem(28行)は,ミニLCDの初期化のためのコマンドが格納されたROMです.
file name: minilcd.v 33 always @(posedge CLK or negedge RST_X) begin 34 if (!RST_X) begin 35 LCD_CS0 <= 0; 36 LCD_CD <= 0; 37 LCD_RSTB <= 0; 38 LCD_D <= 0; 39 init <= 1; 40 cmdcnt <= 0; 41 writecnt <= 0; 42 waitcnt <= 0; 43 end 44 else if (writecnt != 0) writecnt <= writecnt - 1; 45 else if (waitcnt != 0) waitcnt <= waitcnt - 1; 46 else if (init == 1) begin 47 waitcnt <= {dout[15:11], 18'h00000}; 48 LCD_RSTB <= ~dout[10]; 49 LCD_CS0 <= dout[9]; 50 LCD_CD <= dout[8]; 51 LCD_D <= dout[7:0]; 52 init <= (cmdcnt != 'h3a); 53 cmdcnt <= (cmdcnt == 'h3a) ? 0 : cmdcnt + 1; 54 writecnt <= 7; 55 end 56 else begin // after initialized 57 LCD_RSTB <= 1; 58 LCD_CS0 <= 0; 59 LCD_CD <= 1; 60 LCD_D <= (cmdcnt[0]) ? // convert 3 bit to 16 bit color 61 {D[1], D[0], D[0], D[0], D[0], D[0], D[0], D[0]} : 62 {D[2], D[2], D[2], D[2], D[1], D[1], D[1], D[1]} ; 63 cmdcnt <= (cmdcnt == 'h7fff) ? 0 : cmdcnt + 1; 64 writecnt <= 7; 65 end 66 end 67 endmodule |
モジュール minilcd_conの後半です. ミニLCDは16ビットカラーを表示できるので,8bitを2サイクルに分けて送信します. ただし,VRAMの容量の問題から,1ピクセルを3ビット(R, G, Bそれぞれ1ビット)の8色として表現するので, 60行から62行で,3ビットの色を16ビットに変換しています.
file name: minilcd.v 69 /******************************************************************************************************/ 70 /* MiniLCD Video Memory 8KB (128x128=16K pixel, 4bit per pixel = 8KB) */ 71 /******************************************************************************************************/ 72 module minilcd_vram(CLK, DIN, DOUT, RADDR, WADDR, WE); 73 input CLK, WE; 74 input [3:0] DIN; 75 input [13:0] RADDR, WADDR; 76 output reg [3:0] DOUT; 77 78 reg [3:0] mem [16383:0]; /* 4bit x 16K pixel = 8KB VRAM */ 79 80 always @(posedge CLK) begin 81 if (WE) mem[WADDR] <= DIN; 82 DOUT <= mem[RADDR]; 83 end 84 endmodule 85 |
モジュールminilcd_vramです. VRAMは読み出しと書き込みが干渉しないように,読み出しポートを1つ,書き込みポートを1つもつ デュアルポートメモリとして実現されます. それぞれのピクセルには4ビットが割り当てられているので,16色への拡張は可能です.
file name: minilcd.v 86 /******************************************************************************************************/ 87 /* MiniLCD initialize memory, [WAIT(5)] [RST] [CSX] [DC] [DATA(8)] */ 88 /******************************************************************************************************/ 89 module minilcd_initmem(CLK, ADDR, DATA); 90 input CLK; 91 input [5:0] ADDR; 92 output reg [15:0] DATA; 93 94 always @(posedge CLK) begin 95 case (ADDR) 96 'h00: DATA = 16'h1200; 97 'h01: DATA = 16'h1600; // Hardware Reset 98 'h02: DATA = 16'h6200; 99 'h03: DATA = 16'h2801; // Software Reset 100 'h04: DATA = 16'ha011; // Sleep Out 101 'h05: DATA = 16'h00ff; // VCOM 4 Level Control 102 'h06: DATA = 16'h0140; // - TC2 4clk, TC3 3clk delay 158 'h3e: DATA = 16'h0000; 159 'h3f: DATA = 16'h0000; 160 endcase 161 end 162 endmodule |
モジュールminilcd_initmemです.初期化コマンドが格納されているROMです. コマンドが並んでいるだけなので,途中は省略しています.
file name: MipsCore.v 8 /******************************************************************************************************/ 9 /* 32bit-32cycle mutiplier (signed or unsigned) */ 10 /******************************************************************************************************/ 11 module MULUNIT(CLK, RST_X, INIT, SIGNED, A, B, RSLT, BUSY); 12 input CLK; 13 input RST_X; 14 input INIT; 15 input SIGNED; 16 input [31:0] A, B; 17 output [63:0] RSLT; 18 output BUSY; 19 20 wire [31:0] uint_a, uint_b; 21 wire [63:0] uint_rslt; 22 reg sign; 23 24 MULUNITCORE mulcore(.CLK(CLK), .RST_X(RST_X), .INIT(INIT), 25 .A(uint_a), .B(uint_b), .RSLT(uint_rslt), .BUSY(BUSY)); 26 27 assign uint_a = (SIGNED & A[31])? ~A + 1 : A; 28 assign uint_b = (SIGNED & B[31])? ~B + 1 : B; 29 assign RSLT = (SIGNED & sign)? ~uint_rslt + 1 : uint_rslt; 30 31 always @( posedge CLK or negedge RST_X ) 32 if( !RST_X ) sign <= 0; 33 else sign <= (INIT)? A[31]^B[31] : sign; 34 35 endmodule |
MipsCore.vの先頭には,プロセッサコアが利用する幾つかのモジュールが記述されています.
32サイクルで32ビットの乗算をおこなうモジュールMULUNITです. 内部では,次に示すモジュールMULUNITCOREを利用しています.
file name: MipsCore.v 37 /******************************************************************************************************/ 38 module MULUNITCORE(CLK, RST_X, INIT, A, B, RSLT, BUSY); 39 input CLK; 40 input RST_X; 41 input INIT; 42 input [31:0] A, B; 43 output [63:0] RSLT; 44 output BUSY; 45 46 reg [31:0] multiplicand; 47 reg [5:0] count; 48 wire [32:0] sum; 49 reg [63:0] RSLT; 50 51 assign BUSY = (count < 32); 52 assign sum = RSLT[63:32] + multiplicand; 53 54 always @( posedge CLK or negedge RST_X ) begin 55 if( !RST_X ) begin 56 multiplicand <= 0; 57 RSLT <= 0; 58 count <= 0; 59 end else if( INIT )begin 60 multiplicand <= A; 61 RSLT <= {32'h0, B}; 62 count <= 0; 63 end else begin 64 multiplicand <= multiplicand; 65 RSLT <= (RSLT[0]) ? {sum, RSLT[31:1]} : {1'h0, RSLT[63:1]}; 66 count <= count + 1; 67 end 68 end 69 endmodule 70 |
モジュールMULUNITCOREです. 同様に除算のためのモジュールDIVUNIT, DIVUNITCOREがありますが,省略します.
file name: MipsCore.v 142 /******************************************************************************************************/ 143 /* 32bitx32 2R/W General Purpose Registers */ 144 /******************************************************************************************************/ 145 module GPR(CLK, REGNUM0, REGNUM1, DIN0, DIN1, WE0, WE1, DOUT0, DOUT1); 146 input CLK; 147 input [4:0] REGNUM0, REGNUM1; 148 input [31:0] DIN0, DIN1; 149 input WE0, WE1; 150 output [31:0] DOUT0, DOUT1; 151 152 reg [31:0] r[0:31]; 153 reg [31:0] DOUT0, DOUT1; 154 155 always @(posedge CLK) DOUT0 <= (REGNUM0==0) ? 0 : r[REGNUM0]; 156 always @(posedge CLK) DOUT1 <= (REGNUM1==0) ? 0 : r[REGNUM1]; 157 always @(posedge CLK) if(WE0) r[REGNUM0] <= DIN0; 158 always @(posedge CLK) if(WE1) r[REGNUM1] <= DIN1; 159 endmodule |
32ビット,32本のレジスタを持つレジスタファイルを実現するモジュールGPRです. レジスタ番号0(Zeroレジスタ)の読み出しはゼロになるように調整しています.
file name: MipsCore.v 161 /******************************************************************************************************/ 162 /* MipsCore: 32bit-MIPS multicycle processor */ 163 /******************************************************************************************************/ 164 module MIPSCORE(CLK, RST_X, ADDR, DATA_IN, DATA_OUT, WE); 165 input CLK, RST_X; 166 input [7:0] DATA_IN; 167 output [7:0] DATA_OUT; 168 output [`ADDR] ADDR; 169 output WE; 170 /**************************************************************************************************/ 171 /***** internal register *****/ 172 reg [4:0] state; 173 reg [`ADDR] pc, delay_npc, inst_pc, inst_npc; 174 reg [`ADDR] inst_eaddr; 175 reg exec_delay; 176 reg [31:0] inst_ir; 177 reg [4:0] inst_rt, inst_rd, inst_dst; 178 reg [4:0] inst_shamt; 179 reg [15:0] inst_imm; 180 reg [`JADDR] inst_addr; 181 reg [18:0] inst_attr; 182 reg [6:0] inst_op; 183 reg [31:0] inst_rrs, inst_rrt, inst_cpr, inst_rslt, inst_rslthi; 184 reg [31:0] hi, lo; // high and low register for multiply and divide inst. 185 reg inst_cond; 186 reg [3:0] inst_datamask; 187 reg [31:0] inst_loaddata; |
プロセッサであるモジュールMIPSCOREです. 命令やデータの読み出し時は,ADDRを指定して,DATA_INから命令やデータを取得します. データメモリへの書き込み時は,ADDRとDATA_OUTを指定して,WEを1にします. DATA_INとDATA_OUTは8ビット幅としています. [`ADDR] は,define.v で定義されている通り,[23:0] に置き換わります.
file name: MipsCore.v 188 /**************************************************************************************************/ 189 /***** internal wire *****/ 190 reg [4:0] IDRS, IDRT, IDRD; 191 reg [4:0] IDSHAMT; 192 reg [15:0] IDIMM; 193 reg [`JADDR] IDADDR; 194 reg [18:0] IDATTR; 195 reg [6:0] IDOP; 196 wire [4:0] RFDST; 197 reg [31:0] EXRSLT; 198 reg [31:0] EXRSLTHI; 199 reg [`ADDR] EXEADDR; 200 reg [`ADDR] EXNPC; 201 reg EXC; 202 reg [3:0] EXMASK; 203 reg [1:0] EXDATAEXT; 204 reg EXBUSY; 205 wire EXSIGNED; 206 wire [31:0] MALOADDATA, MALOADDATARAW, MALOADMASK; 207 wire [63:0] DURSLT, MURSLT; 208 wire DUBUSY, MUBUSY; 209 wire DIVMULINIT; 210 wire [4:0] GPRNUM0, GPRNUM1, CPRNUM; 211 wire [31:0] GPRREADDT0, GPRREADDT1, CPRREADDT; 212 wire [31:0] GPRWRITEDT0, GPRWRITEDT1, CPRWRITEDT; 213 wire GPRWE0, GPRWE1, CPRWE; 214 wire CPEXCSET, CPEXCCLR, CPEXCACK, CPEXCOCCUR, CPEXCBD; 215 wire [3:0] CPEXCCODE; 216 wire [`ADDR] CPEXCEPC, CPEXCNPC; 217 wire [31:0] SET32I; // 32bit sign extended of 16bit immediate 218 wire [`ADDR] SETADI; // sign extended address of 16bit immediate 219 wire [31:0] RRT_U, RRS_U; 220 wire signed [31:0] RRT_S; 221 wire CPUEXE = 1; // signal to stall the processor! |
内部ワイヤの宣言です.regとして宣言されている IDRSなども,ワイヤとして利用しています.
file name: MipsCore.v 223 /**************************************************************************************************/ 224 /* Sub module declaration */ 225 /**************************************************************************************************/ 226 DIVUNIT du(.CLK(CLK), .RST_X(RST_X), .INIT(DIVMULINIT), .SIGNED(EXSIGNED), 227 .A(GPRREADDT0), .B(GPRREADDT1), .RSLT(DURSLT), .BUSY(DUBUSY)); 228 229 MULUNIT mu(.CLK(CLK), .RST_X(RST_X), .INIT(DIVMULINIT), .SIGNED(EXSIGNED), 230 .A(GPRREADDT0), .B(GPRREADDT1), .RSLT(MURSLT), .BUSY(MUBUSY)); 231 232 GPR gpr(.CLK(CLK), .REGNUM0(GPRNUM0), .REGNUM1(GPRNUM1), .DIN0(GPRWRITEDT0), .DIN1(GPRWRITEDT1), 233 .WE0(GPRWE0), .WE1(GPRWE1), .DOUT0(GPRREADDT0), .DOUT1(GPRREADDT1)); 234 235 MIPSCP0 cp0(.CLK(CLK), .RST_X(RST_X), .REG_NUM(CPRNUM), .REG_IN(CPRWRITEDT), .REG_WE(CPRWE), 236 .REG_OUT(CPRREADDT), .EXC_SET(CPEXCSET), .EXC_CLR(CPEXCCLR), .EXC_ACK(CPEXCACK), 237 .EXC_CODE(CPEXCCODE), .EXC_EPC(CPEXCEPC), .EXC_BD(CPEXCBD), 238 .EXC_OCCUR(CPEXCOCCUR), .EXC_NPC(CPEXCNPC)); |
先に説明した乗算器,除算器,レジスタファイルと,コプロセッサであるMIPSCP0を生成します. オペレーティングシステムやファイルシステム,タイマ割り込みなどを利用しない場合にMIPSCP0は必要ありません. その場合には,236行から238行をコメントアウトしてください.
file name: MipsCore.v 240 /**************************************************************************************************/ 241 /* Mips::proceedstate() */ 242 /**************************************************************************************************/ 243 always @(posedge CLK or negedge RST_X) begin 244 if(!RST_X) state <= `CPU_START; 245 else if(~CPUEXE || state==`CPU_HALT) state <= state; 246 else if(exec_delay && delay_npc==`HALT_ADDR) state <= `CPU_HALT; 247 else if(state==`CPU_EX && EXBUSY) state <= state; 248 else if(state==`CPU_EX && ~EXBUSY) state <= (inst_attr & `LDST) ? `CPU_MA0 : `CPU_WB; 249 else if(state==`CPU_WB) state <= `CPU_IF0; 250 else state <= state + 1; 251 end |
命令処理のための状態stateを更新する部分です. このプロセッサは,(パイプライン処理をおこなわない)マルチサイクル版です. ロード命令,ストア命令の場合には,IF0, IF1, IF2, IF3, IF4, ID, RF, EX, MA0, MA1, MA2, MA3, MA4, WB というステートを経由して, 1つの命令を処理するので,ロード命令の処理には14サイクルを必要とします. データメモリにアクセスしない加算命令などでは, IF0, IF1, IF2, IF3, IF4, ID, RF, EX, WB というステートを経由して,9サイクルを必要とします. 248行において,LD/ST命令の時は,MA0に,そうでない場合には,WBに進むとしています.
file name: MipsCore.v 253 /**************************************************************************************************/ 254 /* Mips:: instruction fetch stage and memory access address generation */ 255 /**************************************************************************************************/ 256 // inst_eaddr is incremented on MA0, MA1, and MA2 stage 257 assign ADDR = (state == `CPU_IF0) ? (pc & ~'h3) : 258 (state == `CPU_IF1) ? (pc & ~'h3) | 2'h1 : 259 (state == `CPU_IF2) ? (pc & ~'h3) | 2'h2 : 260 (state == `CPU_IF3) ? (pc & ~'h3) | 2'h3 : 261 (state >= `CPU_MA0 && state <= `CPU_MA3) ? inst_eaddr : 0; 262 263 always @( posedge CLK or negedge RST_X ) begin 264 if(!RST_X) inst_pc <= 0; 265 else if(CPUEXE) begin 266 if (state==`CPU_IF0) inst_pc <= pc; 267 if (state==`CPU_IF1) inst_ir[ 7: 0] <= DATA_IN; 268 else if(state==`CPU_IF2) inst_ir[15: 8] <= DATA_IN; 269 else if(state==`CPU_IF3) inst_ir[23:16] <= DATA_IN; 270 else if(state==`CPU_IF4) inst_ir[31:24] <= DATA_IN; 271 end 272 end |
257行から261では,命令フェッチのためのアドレスとロード,ストア命令のためのアドレスADDRを選択しています. IF0からIF3では,プログラムカウンタ pc の値から1バイトごとに読み出します(257行から260行). MA0からMA3では,EXで計算する inst_eaddr を利用します.
IF1からIF4のステージで得られた4バイトを267行から270行で命令レジスタ inst_ir に格納して,命令フェッチを 終了します.
file name: MipsCore.v 274 /**************************************************************************************************/ 275 /* Mips:: instruction decode stage */ 276 /**************************************************************************************************/ 277 always@ (inst_ir) begin 278 IDRS = inst_ir[25:21]; 279 IDRT = inst_ir[20:16]; 280 IDRD = inst_ir[15:11]; 281 IDSHAMT = inst_ir[10:6]; 282 IDIMM = inst_ir[15:0]; 283 IDADDR = inst_ir[25:0]; 284 IDOP = `NOP______; 285 IDATTR = `WRITE_NONE; 286 287 case (inst_ir[31:26]) // OP 288 6'd00: case (inst_ir[5:0]) // FUNCT 318 6'd32: begin IDOP=`ADD______; IDATTR=`WRITE_RD; end 319 6'd33: begin IDOP=`ADDU_____; IDATTR=`WRITE_RD; end 320 6'd34: begin IDOP=`SUB______; IDATTR=`WRITE_RD; end 321 6'd35: begin IDOP=`SUBU_____; IDATTR=`WRITE_RD; end 322 6'd36: begin IDOP=`AND______; IDATTR=`WRITE_RD; end 323 6'd37: begin IDOP=`OR_______; IDATTR=`WRITE_RD; end 324 6'd38: begin IDOP=`XOR______; IDATTR=`WRITE_RD; end 325 6'd39: begin IDOP=`NOR______; IDATTR=`WRITE_RD; end 326 6'd42: begin IDOP=`SLT______; IDATTR=`WRITE_RD; end 327 6'd43: begin IDOP=`SLTU_____; IDATTR=`WRITE_RD; end 328 endcase 373 6'd43: begin IDOP=`SW_______; IDATTR=`STORE_4B_ALIGN; end 374 6'd46: begin IDOP=`SWR______; IDATTR=`STORE_4B_UNALIGN; end 375 endcase 376 end 377 378 always @( posedge CLK or negedge RST_X ) begin 379 if(!RST_X) {inst_rt, inst_rd, inst_shamt, inst_imm, inst_addr, inst_attr, inst_op} <= 0; 380 else if(CPUEXE && state==`CPU_ID) begin 381 inst_rt <= IDRT; 382 inst_rd <= IDRD; 383 inst_shamt <= IDSHAMT; 384 inst_imm <= IDIMM; 385 inst_addr <= IDADDR; 386 inst_attr <= IDATTR; 387 inst_op <= IDOP; 388 end 389 end |
命令デコードです. 命令レジスタをフィールドごとに切り出して,処理している命令を解析します.
この部分も長いので途中で省略しています. 特に inst_op には,それぞれの命令に割り当てられた固有のIDを格納します. 例えば,inst_ir[31:26]のOPフィールドが0で,inst_ir[5:0]のFUNCTフィールドが6'd32であれば,ADD命令なので, 318行でIDOP=`ADD______; が設定され,387行で,inst_op に格納されます.
file name: MipsCore.v 391 /**************************************************************************************************/ 392 /* Mips:: register file access */ 393 /**************************************************************************************************/ 394 assign GPRNUM0 = (state==`CPU_ID) ? IDRS : inst_dst; // use IDRS if reg_read else write to inst_dst 395 assign GPRNUM1 = (state==`CPU_ID) ? IDRT : 'd7; // use IDRT if reg_read else SYSCALL $7<=0 396 assign CPRNUM = IDRD; 397 assign RFDST = ((inst_attr & `WRITE_RD ) || (inst_attr & `WRITE_RD_COND)) ? inst_rd : 398 (inst_attr & `WRITE_RT ) ? inst_rt : 399 (inst_attr & `WRITE_RRA) ? 'd31 : (inst_attr & `SYSTEM_CALL) ? 'd2 : 'd0; 400 401 always @( posedge CLK or negedge RST_X ) begin 402 if(!RST_X) {inst_rrs, inst_rrt, inst_cpr, inst_dst} <= 0; 403 else if(CPUEXE && state==`CPU_RF) begin 404 inst_rrs <= GPRREADDT0; 405 inst_rrt <= GPRREADDT1; 406 inst_cpr <= CPRREADDT; 407 inst_dst <= RFDST; 408 end 409 end |
レジスタアクセスのコードです.レジスタファイルから読み出した32ビットのオペランドをinst_rrs, inst_rrtに 格納します(404行,405行) inst_dst は,その命令が結果を書き込むレジスタの番号(5ビット)です.
file name: MipsCore.v 413 /**************************************************************************************************/ 414 /* Mips:: execute */ 415 /**************************************************************************************************/ 422 assign SET32I = (inst_imm[15]) ? {16'hffff, inst_imm} : {16'h0000, inst_imm}; 423 assign SETADI = SET32I[`ADDR]; 424 assign RRT_S = inst_rrt; 425 assign RRS_U = inst_rrs; 426 assign RRT_U = inst_rrt; 427 assign EXSIGNED = (inst_op == `MULT_____ || inst_op == `DIV______); 428 429 always @(DUBUSY or DURSLT or MUBUSY or MURSLT or inst_shamt or inst_imm or inst_addr or 430 inst_op or inst_pc or inst_rrs or inst_rrt or SET32I or SETADI or 431 RRT_S or RRS_U or RRT_U or hi or lo or inst_cpr) begin 432 EXRSLT = 0; 433 EXRSLTHI = 0; 434 EXNPC = 0; 435 EXC = 0; 436 EXEADDR = 0; 437 EXMASK = 0; 438 EXDATAEXT = 0; 439 EXBUSY = 0; 440 case ( inst_op ) 463 `DIV______ : begin {EXRSLTHI, EXRSLT} = (RRT_U) ? DURSLT : 0; EXBUSY = DUBUSY; end 464 `DIVU_____ : begin {EXRSLTHI, EXRSLT} = (RRT_U) ? DURSLT : 0; EXBUSY = DUBUSY; end 465 `ADD______ : begin EXRSLT = RRS_U + RRT_U; end 466 `ADDU_____ : begin EXRSLT = RRS_U + RRT_U; end 546 always @( posedge CLK or negedge RST_X ) begin 547 if (!RST_X) 548 {inst_rslt, inst_rslthi, inst_eaddr, inst_cond, inst_npc, inst_datamask} <= 0; 549 else if (CPUEXE && state==`CPU_EX ) begin 550 inst_rslt <= EXRSLT; 551 inst_rslthi <= EXRSLTHI; 552 inst_eaddr <= EXEADDR; 553 inst_cond <= EXC; 554 inst_npc <= EXNPC; 555 inst_datamask <= EXMASK; 556 end else if (CPUEXE && state >= `CPU_MA0 && state <= `CPU_MA2) begin 557 inst_eaddr <= inst_eaddr + 1; 558 end 559 end |
実行ステージのコードです.一部省略しています.例えば加算命令の加算は,465行にておこなわれます. 計算結果は550行で inst_rsltに格納されます.
file name: MipsCore.v 561 /**************************************************************************************************/ 562 /* Mips:: memory access */ 563 /**************************************************************************************************/ 564 ///// memory store 565 assign DATA_OUT = (state == `CPU_MA0) ? inst_rslt[ 7: 0] : 566 (state == `CPU_MA1) ? inst_rslt[15: 8] : 567 (state == `CPU_MA2) ? inst_rslt[23:16] : 568 (state == `CPU_MA3) ? inst_rslt[31:24] : 0; 569 570 assign WE = (CPUEXE && (inst_attr & `STORE_ANY) && 571 ((state == `CPU_MA0 && inst_datamask[0]) || 572 (state == `CPU_MA1 && inst_datamask[1]) || 573 (state == `CPU_MA2 && inst_datamask[2]) || 574 (state == `CPU_MA3 && inst_datamask[3]))); 575 576 always @( posedge CLK or negedge RST_X ) begin ///// memory load 577 if (!RST_X) inst_loaddata <= 0; 578 else if (CPUEXE) 579 if (state == `CPU_MA1) inst_loaddata[ 7: 0] <= DATA_IN; 580 else if (state == `CPU_MA2) inst_loaddata[15: 8] <= DATA_IN; 581 else if (state == `CPU_MA3) inst_loaddata[23:16] <= DATA_IN; 582 else if (state == `CPU_MA4) inst_loaddata[31:24] <= DATA_IN; 583 end |
メモリアクセスステージです.
file name: MipsCore.v 585 /**************************************************************************************************/ 586 /* Mips:: write back */ 587 /**************************************************************************************************/ 588 assign MALOADDATARAW = inst_loaddata[31:0]; 589 assign MALOADMASK = {{8{inst_datamask[3]}}, {8{inst_datamask[2]}}, 590 {8{inst_datamask[1]}}, {8{inst_datamask[0]}}}; 591 assign MALOADDATA = (EXDATAEXT[0]) ? {{24{MALOADDATARAW[ 7]}}, MALOADDATARAW[ 7:0]} : 592 (EXDATAEXT[1]) ? {{16{MALOADDATARAW[15]}}, MALOADDATARAW[15:0]} : 593 (MALOADDATARAW & MALOADMASK) | (inst_rslt & ~MALOADMASK); 594 595 assign GPRWE0 = ((CPUEXE && state==`CPU_WB && ~CPEXCOCCUR) && 596 ~((inst_attr & `WRITE_RD_COND) && inst_cond==0)); 597 assign GPRWE1 = ((CPUEXE && state==`CPU_WB && ~CPEXCOCCUR) && (inst_attr & `SYSTEM_CALL)); 598 assign CPRWE = ((CPUEXE && state==`CPU_WB && ~CPEXCOCCUR) && (inst_attr & `WRITE_CP0)); 599 assign GPRWRITEDT0 = (inst_attr & `LDST) ? MALOADDATA : inst_rslt; 600 assign GPRWRITEDT1 = 32'h0; // 2nd write port is just for SYSCALL (REG_A3 <= 0) 601 assign CPRWRITEDT = inst_rslt; 602 603 604 always @( posedge CLK or negedge RST_X ) begin 605 if(!RST_X) {hi, lo} <= 0; 606 else if(CPUEXE && state==`CPU_WB && ~CPEXCOCCUR) begin 607 if(inst_attr & `WRITE_HI) hi <= inst_rslthi; 608 if(inst_attr & `WRITE_LO) lo <= inst_rslt; 609 end 610 end 611 |
ライトバックステージの前半です.レジスタファイルに書き込む信号を作っています.
file name: MipsCore.v 612 /**************************************************************************************************/ 613 /* Mips:: setnpc() */ 614 /**************************************************************************************************/ 615 assign CPEXCSET = (CPUEXE && state==`CPU_EX && (inst_attr & `SYSTEM_CALL)); 616 assign CPEXCCLR = (CPUEXE && state==`CPU_EX && (inst_attr & `BRANCH_ERET)); 617 assign CPEXCACK = (CPUEXE && state==`CPU_WB && CPEXCOCCUR); 618 assign CPEXCCODE = 4'd8; // EXC_SYSCALL 619 assign CPEXCEPC = (exec_delay) ? pc - 4 : pc; 620 assign CPEXCBD = exec_delay; 621 622 always @( posedge CLK or negedge RST_X ) begin 623 if(!RST_X) {pc, delay_npc, exec_delay} <= 0; 624 else if(CPUEXE && state == `CPU_WB) begin 625 if (CPEXCOCCUR) begin 626 pc <= CPEXCNPC; 627 exec_delay <= 0; 628 end else if( exec_delay ) begin 629 pc <= delay_npc; 630 delay_npc <= 0; 631 exec_delay <= 0; 632 end else if( ((inst_attr & `BRANCH) || (inst_attr & `BRANCH_LIKELY)) && inst_cond ) begin 633 pc <= pc + 4; 634 delay_npc <= inst_npc; 635 exec_delay <= 1; 636 end else if (inst_attr & `BRANCH_ERET) begin 637 pc <= CPEXCNPC; 638 end else if( (inst_attr & `BRANCH_LIKELY) && !inst_cond ) begin 639 pc <= pc + 8; 640 end else begin 641 pc <= pc + 4; 642 end 643 end 644 end 645 endmodule |
ライトバックステージの後半です.次に処理する命令のためにプログラムカウンタを更新します. 遅延分岐のため,制御が複雑になっています.
これでプロセッサのコードMipsCore.vはおしまいです.
このプロセッサでqn24b(N-queensの解の数を求めるプログラム)を動かしてみました.
開発キットには130_qn24bのディレクトリにソースコードがあります.
gcc version 4.3.6 (Buildroot 2011.08), 最適化オプションO2でコンパイルして,
N=13を実行したところ,実行時間は 29.70秒でした.
とても遅いです.高速化に挑戦してみてください.
Copyright(c) 2011 Tokyo Tech Kise Laboratory. All rights reserved.