구현한 RV32I verilog module들을 waveform으로 잘 작동하는 지 확인한다.
크게 두가지 경우만 포스팅하겠다.
1. ImmGen
2. ALU_Control
`timescale 1ns/1ps
module tb_XXX; // XXX자리에 모듈 이름
// Dut은 simulation target source code
// 1) DUT port와 같은 타입/폭으로 reg/wire 선언
// reg : TB에서 값을 넣어주는 쪽 (즉 DUT의 input)
// wire : DUT에서 나오는 쪽 (즉 DUT의 output)
// 2) DUT의 인스턴스
// XXX DUT( ... );
.a(A),...
//a-> XXX모듈에 정의된 이름
//A -> 현재 스코프(tb)에 선언된 변수명
// 3) 파형 덤프
initial begin
$dumpfile("waves.vcd"); // simv 작동한 wave.vcd 파일 생성 코드
$dumpvars(0, tb_XXX);//모듈의 모든 신호 변화 기록
end
// 4) 자극주기 ( initial/awlays block)
initial begin
//입력 변화 주고
//#딜레이로 시간 흘러보내고
$finish;
end
endmodule
module ImmGen(
input wire [31:0] instr,
input wire [2:0] immSrc,
output reg [31:0] Imm
);
/*
immSrc
000 : I-type
001 : S-type
010 : B-type
011 : J-type
100 : U-type
101, 110, 111 : are not used
*/
wire [31:0] imm_i = {{20{instr[31]}},instr[31:20]};
wire [31:0] imm_s = {{20{instr[31]}},instr[31:25],instr[11:7]};
wire [31:0] imm_b = {{20{instr[31]}},instr[7], instr[30:25], instr[11:8], 1'b0};
wire [31:0] imm_j = {{12{instr[31]}},instr[19:12],instr[20],instr[30:21],1'b0};
wire [31:0] imm_u = {instr[31:12],12'b0};
always @(*) begin
case(immSrc)
3'b000 : Imm = imm_i;
3'b001 : Imm = imm_s;
3'b010 : Imm = imm_b;
3'b011 : Imm = imm_j;
3'b100 : Imm = imm_u;
default : Imm = 32'd0;
endcase
end
endmodule
instr값을 고정시키고,immSrc에 따라 각 type별Imm값이 잘 도출되는지 확인한다.
instr는 모든 타입을 구별할 수 있는 적당한 값으로 설정한다.
(32'b 1010 1010 1010 1010 1010 1010 1010 1010)
immSrc의 값만 변화를 주어 type별 Imm 값 확인한다. (3'b000 으로 초기값 설정 후 100ns 마다 1 씩 증가시켰다.
ImmGen.v에 따라 각 type별 예상되는 값은 다음과 같다.
I-type : FFFFFAAA (1111 1111 1111 1111 1111 1010 1010 1010)
S-type : FFFFFAB5 (1111 1111 1111 1111 1111 1010 1011 0101)
B-type : FFFFFAB4 (1111 1111 1111 1111 1111 1010 1011 0100)
J-type : FFFAA2AA (1111 1111 1111 1010 1010 0010 1010 1010)
U-type : AAAAA000 (1010 1010 1010 1010 1010 0000 0000 0000)
`timescale 1ns/1ps
module tb_ImmGen;
reg [31:0] instr;
reg [2:0] immSrc;
wire [31:0] Imm;
integer i;
ImmGen DUT(
.instr(instr),
.immSrc(immSrc),
.Imm(Imm)
);
initial begin
$dumpfile("waves_ImmGen.vcd");
$dumpvars(0,tb_ImmGen);
end
initial instr =32'b10101010101010101010101010101010;
initial begin
immSrc =3'b000;
for(i=0;i<4;i=i+1) begin
#100
immSrc=immSrc+1;
end
end
initial begin
#1000
$finish;
end
endmodule

예상대로 잘 도출되었음을 확인할 수 있다.
Input : [31:0] Instr, [1:0] Alu_Op
output : [3:0] alu_op
Function : Instr의 fucnt 값과, Control unit으로 부터 받은 ALU_Op 값을 기반으로 ALU에 연산을 결정하는 alu_op를 결정한다.
밑에 모든 경우는 동시에 일어나게 설계한다.
*100 ns* 마다 [1:0] Alu_Op`를 1씩 증가시켜 업데이트 한다.Alu_op = 2'b10, 2'b11 일 경우, R-type과 I-type의 연산을 확인해야한다. 따라서 10 ns 마다 funct3값을 변화시킨다.
더불어, 5 ns 마다 funct7[5]의 값을 변화시켜 모든 경우의 연산을 확인한다.
`timescale 1ns/1ps
module tb_ALU_Control;
reg [31:0] instr;
reg [1:0] in_alu_op;
wire [3:0] out_alu_op;
ALU_Control DUT(
.Instr(instr),
.ALU_Op(in_alu_op),
.alu_op(out_alu_op)
);
initial begin
$dumpfile("waves_ALU_Control.vcd");
$dumpvars(0,tb_ALU_Control);
end
initial begin
instr =32'h00000000;
in_alu_op=2'b00;
end
initial begin
forever begin
#100;
in_alu_op=in_alu_op+2'b01;
instr[14:12] = 3'b111;
end
end
initial begin
forever begin
#5;
instr[30] = ~instr[30];
end
end
initial begin
forever begin
#10;
instr[14:12] = instr[14:12]+3'b001;
end
end
initial begin
#1000;
$finish;
end
endmodule
Figure 2. ALU_Op = 00 일 때 결과
Figure 3. ALU_Op = 01 일 때 결과
Figure 4. ALU_Op = 10 일 때 결과
Figure 5. ALU_Op = 11 일 때 결과
예상대로 잘 도출되었음을 확인할 수 있다.