wiki:Ex06課題2015

Version 6 (modified by nakasato, 11 years ago) (diff)

--

トップ:http://galaxy.u-aizu.ac.jp/note/wiki/CAEX2015

レジスタファイルについて

MIPSプロセッサにおいて、プログラマから見える汎用レジスタには $0 から $31 までの32個があります。 このうち $0 は値が常にゼロである仮想的なレジスタなので、実際のハードウェアでは31個のレジスタからなるレジスタファイルが使われます。 各レジスタの入出力データのビット幅は32ビットです。

レジスタファイルに対する書き込み口や読み出し口のことをポートと呼びます。 今回利用するレジスタファイルは2つの読み出しポートと1つの書き込みポートを備えています。 このことは、2つの異なるレジスタの読み出しと、さらに異なる1つのレジスタへの書き込みを、 1つのクロックサイクルの中で同時に行えるということを意味します。

レジスタファイルの記述例:Registers.v

module Registers(ReadRegster1, ReadRegster2, WriteData, RegWrite, WriteRegster, CK, CLR, ReadData1, ReadData2);

input[4:0] 	ReadRegster1, ReadRegster2, WriteRegster;
input[31:0] 	WriteData;
input		RegWrite, CK, CLR;
output[31:0]	ReadData1, ReadData2;

reg[31:0]	regfile[1:31];
integer i;

always	@(posedge CK) begin
      if (CLR == 1'b1)
            for( i = 1; i < 31; i = i + 1)
                  regfile[i] <= 32'h00000000;
      else if ( RegWrite == 1'b1 && WriteRegster != 5'b00000)
                  regfile[WriteRegster] <= WriteData;
      end
 

assign #5 ReadData1 = (ReadRegster1 == 5'b00000) ? 32'h00000000 : regfile[ReadRegster1];
assign #5 ReadData2 = (ReadRegster2 == 5'b00000) ? 32'h00000000 : regfile[ReadRegster2];

endmodule

入出力信号の説明

  • ReadRegster1 と ReadData1 は1つ目の読み出しポートのための信号です。ReadRegster1 にレジスタ番号を (0~31) を入力すると該当するレジスタの値が ReadData1 から出力されます(レジスタ番号が 0 のときは常に 0 が出力される)。
  • ReadRegster2 と ReadData2 は2つ目の読み出しポートのための信号です。動作は1つ目の読み出しポートと同じです。
  • WriteData?RegWrite?、およびWriteRegsterは書き込みポートを形成します。WriteRegster? にレジスタ番号を入力し、WriteDataには書き込むデータをセットして、RegWrite? を 1 にすると、次のクロックの立ち上がりでデータが該当レジスタに書き込まれます。WriteEN が 0 のときは、クロックの立ち上がりがあってもデータは書き込まれません。また、レジスタ番号が 0 のときは、レジスタへの書き込みは行われません。
  • CK はクロック信号、CLR はクリア信号です。 CLR が 1 になると全てのレジスタが 0 に初期化されます。

メモリについて

課題1 レジスタの利用方法

以下のテストベンチは、レジスタファイルをインスタンス化して、$a0レジスタに"0xffffffff"を書き込んでいる。

`timescale 1ns/1ps
`include "Registers.v"

module  RegTest;
   reg [4:0]  read_adr1,  read_adr2,  write_adr;
   reg [31:0] write_data;
   reg        write_enable;
   reg        ck,clear;
   wire [31:0] read_data1, read_data2;

   Registers regs(read_adr1, read_adr2, write_data, write_enable, write_adr, ck, clear, read_data1, read_data2);

   initial
     begin
        $dumpfile("RegTest.vcd");
        $dumpvars(0, RegTest);


        #0
        ck = 1'b1;
        clear = 1'b1;

        #110
        clear = 1'b0;

        #190
        read_adr1 = 5'b00100;

        #100
        write_enable = 1'b1;
        write_adr  = 5'b00100;
        write_data = 32'hffffffff;
        $display("%x %x",read_adr1, read_data1);

        #100
        write_enable = 1'b0;
        $display("%x %x",read_adr1, read_data1);

        #900

        $finish;
     end // initial begin

   always #50 ck = ~ck;
endmodule

これを参考にして、以下のテーブルに示すように、レジスタに値の値を書き込んで、読み出すテストベンチを作成し実行したうえで、クロック信号(ck)、読み出しているレジスタのアドレス(read_adr1)と読み出し結果(read_data1)の波形を確認しなさい。波形には、この三つの信号のみを表示すること。表示する順番は、小さいアドレスから大きいアドレスになるようにすること。なお、表示している信号名がわかるようにすること。

レジスタ
$sp 0xfffff
$v0 0xfafa
$v1 0x1010
$t0 0xffffffff
$t1 0xaaaaaaaa
$ra 0x98765432
$t2 0x98765432
$t3 0x12121

波形の例

以下のように各レジスタにデータを書き込んだ場合:

$at 0xf
$v0 0xff
$v1 0xfff
$a0 0xffff

実行結果の波形の例:

http://galaxy.u-aizu.ac.jp/note/raw-attachment/wiki/Ex06%E8%AA%B2%E9%A1%8C2015/comparch_ex6_reg.png

課題2 メモリの利用方法

以下のテストベンチファイルでは、レジスタファイルとメモリとALUをインスタンス化し、テストするためのテンプレートである。

`timescale 1ns/1ps
`include "Registers.v"
`include "Memory.v"
`include "ALU.v"

module  MemTest;
   // for Memory
   reg [31:0] address;
   reg [31:0] write_data;
   reg        write_enable, read_enable;
   reg        ck;
   wire [31:0] read_data;

   // for Registers
   reg [4:0]  read_adr1_reg, read_adr2_reg, write_adr_reg;
   reg [31:0] write_data_reg;
   reg        write_enable_reg;
   reg        clear_reg;
   wire [31:0] read_data1_reg, read_data2_reg;

   // for ALU
   reg [31:0]  A, B;
   reg [3:0]   ALUop;
   wire        Zero;
   wire [31:0] Result;
      
   Memory    mem(address, write_data, read_data, write_enable, read_enable, ck);

   Registers regs(read_adr1_reg, read_adr2_reg, write_data_reg, write_enable_reg, write_adr_reg,
		  ck, clear_reg,
		  read_data1_reg, read_data2_reg);

   ALU       alu(A, B, ALUop, Result, Zero);
      
   initial
     begin
        $dumpfile("MemTest.vcd");
        $dumpvars(0, MemTest);
	
	// data segment
	Mem.cell['h00005000] = 32'h0000000a;
	Mem.cell['h00005004] = 32'h0000000b;
	Mem.cell['h00005008] = 32'h00000000;
	Mem.cell['h00005010] = 32'h00000000;
	Mem.cell['h00005014] = 32'h00000000;
	Mem.cell['h00005018] = 32'h00000000;

        #0
        ck = 1'b1;
        clear_reg = 1'b1;
	
	read_enable = 1'b0;
	address = 32'h00000000;
	write_enable_reg = 1'b0;	
	write_adr_reg    = 5'b00000;	
	write_data_reg   = 32'h00000000;
	
        #110
        clear_reg = 1'b0;

        #190 // read memory at 0x5000
	read_enable = 1'b1;
    	address = 32'h00005000;

	#100 // write data to $v0 and read memory at 0x5004
        write_enable_reg = 1'b1;
	write_adr_reg    = 5'b00010;
	write_data_reg   = read_data;

	#100
	read_enable = 1'b0;
        write_enable_reg = 1'b0;

	#100
	read_adr1_reg = 5'b00010;

	#100
        $display("$v0 = %x",read_data1_reg);	  

	#2000
	
        $finish;
     end // initial begin
   
   always #50 ck = ~ck;
endmodule

実行結果は以下のようになるはずである。

% ncverilog test_mem.v

....省略

ncsim> run
$v0 = 0000000a

....省略

ちなみに、以下の部分でメモリからデータを読み出し、レジスタファイル($v0)に書き込んでいる。

        #190 // read memory at 0x5000
        read_enable = 1'b1;
        address = 32'h00005000;

        #100 // write data to $v0
        write_enable_reg = 1'b1;
        write_adr_reg    = 5'b00010;
        write_data_reg   = read_data;

        #100
        read_enable = 1'b0;
        write_enable_reg = 1'b0;

実行結果の波形の例:

http://galaxy.u-aizu.ac.jp/note/raw-attachment/wiki/Ex06%E8%AA%B2%E9%A1%8C2015/comparch_ex6_mem.png

このテンプレートを元にして、アドレス0x5000と0x5004にあるデータを演算した結果を、再びメモリに書き込むテストベンチを作成せよ。格納アドレスと演算結果の対応は以下の通りとする。特に、レジスタへの読み書き、演算をして結果をメモリに書き込む部分の波形を確認すること。

0x5008 加算
0x500c 減算
0x5010 AND演算
0x5014 3*"0x5000のデータ"+"0x5004のデータ"

Attachments (2)

Download all attachments as: .zip