[Harman교육] 베릴로그

[23.03.23] CPU 설계 - datapath

블링블링이즈정원 2023. 3. 23. 20:57

1. function unit

1-1

만들어 놓은 ALU와 Shifter를 합쳐 아래와 같이 function unit을 작성했다.

module Func_unit (oprA, oprB, FS, SA, Fout, v_flag, c_flag, n_flag, z_flag);
parameter BW = 16; 
input [BW-1:0] oprA, oprB;
input [4:0] FS;
input [2:0] SA;
output [BW-1:0] Fout;
output  v_flag, c_flag, n_flag, z_flag;

wire [BW-1:0] sft_out, alu_out;
wire [3:0] mux_sel;
assign mux_sel = {FS[0],SA[2:0]};

shifter shifter_u1 (.mux_sel(mux_sel), .oprB(oprB), .sel_mode(FS[3:1]), .Y(sft_out));
ALU alu_u1 (.oprA(oprA), .oprB(oprB), .Cin(FS[0]), .sel(FS[3:1]), .gout(alu_out), .v_flag(v_flag), .c_flag(c_flag), .n_flag(n_flag), .z_flag(z_flag));

assign Fout = FS[4] ? sft_out : alu_out;  //FS[4] = MF_select

endmodule

MUX F를 보면 MF 값이 0이면 alu의 출력이 최종출력되고, 1이면 shifter의 출력이 최종출력된다. 이를 assign문으로 간단하게 나타내주었다. 

 

1-2 시뮬레이션

module tb_func_unit;
reg [15:0] oprA, oprB;
reg [4:0] FS;
reg [2:0] SA;
wire [15:0] Fout;
wire v_flag, c_flag, n_flag, z_flag;

 Func_unit u1 (oprA, oprB, FS, SA, Fout, v_flag, c_flag, n_flag, z_flag);
 
 initial begin 
        FS = 5'b00000; oprA = 16'h4; oprB = 16'h8; 
#10     FS = 5'b00001; oprA = 16'h4; oprB = 16'h8;
#10     FS = 5'b00010; oprA = 16'h4; oprB = 16'h8;
#10     FS = 5'b00011; oprA = 16'h4; oprB = 16'h8;
#10     FS = 5'b00100; oprA = 16'h4; oprB = 16'h8;
#10     FS = 5'b00101; oprA = 16'h4; oprB = 16'h8;
#10     FS = 5'b00110; oprA = 16'h4; oprB = 16'h8;
#10     FS = 5'b00111; oprA = 16'h4; oprB = 16'h8;
#10     FS = 5'b01000; oprA = 16'h4; oprB = 16'h8;
#10     FS = 5'b01011; oprA = 16'h4; oprB = 16'h8;
#10     FS = 5'b01100; oprA = 16'h4; oprB = 16'h8;  //ALU finish
#20     FS = 5'b10000; oprA = 16'h4; oprB = 16'b1000_0000_0000_1111; SA = 2'b011;// SFT - hold 
#10     FS = 5'b10010; oprA = 16'h4; oprB = 16'b1000_0000_0000_1111; SA = 2'b011; //1bit shift left (ignore oprA, SA)
#10     FS = 5'b10100; oprA = 16'h4; oprB = 16'b1000_0000_0000_1111; SA = 2'b011; //1bit shift right
#10     FS = 5'b10110; oprA = 16'h4; oprB = 16'b1000_0000_0000_1111; SA = 2'b011; //arith shift right
#10     FS = 5'b11000; oprA = 16'h4; oprB = 16'b1000_0000_0000_1111; SA = 2'b011; //1bit rotate left
#10     FS = 5'b11010; oprA = 16'h4; oprB = 16'b1000_0000_0000_1111; SA = 2'b011; //1bit rotate right
#10     FS = 5'b11100; oprA = 16'h4; oprB = 16'b1000_0000_0000_1111; SA = 2'b011; // SA_3bit rotate right
#10     FS = 5'b11110; oprA = 16'h4; oprB = 16'b1000_0000_0000_1111; SA = 2'b011; // //SA_3bit rotate left //SFT finish
#10;
$stop;
end
 
 endmodule

시뮬레이션 결과가 정상적으로 나온 것을 확인하였다.

 

 

2. Register file 

2-1

 

Register file을 구현하기 위해 Decoder 와 D-f/f, 8x1 MUX를 먼저 만들었다. 이때 load enable을 decoder 블럭에서 enable로써 사용하였다.

module reg_16b (clk, start, enb, din, dout);
parameter	DW = 16;
input					clk, start, enb;
input		[DW-1:0]	din;
output	reg	[DW-1:0]	dout;

always @ (posedge clk or negedge start)
begin
	if (!start)
		dout <= {DW{1'b0}};
	else if (enb)
		dout <= din;
end

endmodule

module mux8x1 (sel,d0,d1,d2,d3,d4,d5,d6,d7,dout);
 parameter BW = 16;
 input [2:0]      sel;
 input [BW-1:0]           d0, d1, d2, d3, d4, d5, d6, d7;
 output reg [BW-1:0]         dout;

 
 always @ (sel,d0,d1,d2,d3,d4,d5,d6,d7)
 begin
 if          (sel==3'b000) dout = d0;
 else if     (sel==3'b001) dout = d1;
 else if     (sel==3'b010) dout = d2;
 else if     (sel==3'b011) dout = d3;
 else if     (sel==3'b100) dout = d4;
 else if     (sel==3'b101) dout = d5;
 else if     (sel==3'b110) dout = d6;
 else dout = d7;  
 end
 
 endmodule

D-f/f, 8x1 MUX이다.

 

다음 디코더 구현이다.

기본 컨셉은, 일단 8개의 레지스터의 Din에는 func unit의 output값이 들어온다. 이 때 R/W 신호에 따라 레지스터에 저장할 수 있도록 디코더에 en을 만들어줬다. (R/W = load enable) decoder의 출력은 8비트인데, 이게 한 비트씩 레지스터의 en으로 들어간다. 예를 들어 000을 디코더의 sel신호로 주면, 00000001이 출력되고 dec_out[0]만 1이기 때문에 R0만 Din이 Q로 나가서 MUX로 들어가게 된다.

module dec_3to8 (load_en, sel, dec_out);
input load_en;
input [2:0] sel;
output reg [7:0] dec_out;


always @ (load_en, sel)
begin
    if (load_en)
        case (sel)
        3'b000 : dec_out = 8'b0000_0001;
        3'b001 : dec_out = 8'b0000_0010;
        3'b010 : dec_out = 8'b0000_0100;
        3'b011 : dec_out = 8'b0000_1000;
        3'b100 : dec_out = 8'b0001_0000;
        3'b101 : dec_out = 8'b0010_0000;
        3'b110 : dec_out = 8'b0100_0000;
        default : dec_out = 8'b1000_0000;
        endcase    
    else
    dec_out <= 8'b0;
end

endmodule

 

2-2 Reg_file

module Reg_file (clk, SA, SB, DR, RW, start, Fout, oprB, oprA, r0_reg); //r0_reg
parameter BW = 16;
input clk, RW, start;
input [2:0] SA, SB, DR;
input [BW-1:0] Fout;
output [BW-1:0] oprA, oprB;
output wire [BW-1:0] r0_reg;

wire [7:0] dec_out;
wire [15:0] reg_out [0:7];

assign r0_reg = reg_out[0];

dec_3to8 dec_3to8_u (.load_en(RW), .sel(DR), .dec_out(dec_out));
mux8x1 MUX_A (.sel(SA), .d0(reg_out[0]), .d1(reg_out[1]), .d2(reg_out[2]), .d3(reg_out[3]), .d4(reg_out[4]), .d5(reg_out[5]), .d6(reg_out[6]), .d7(reg_out[7]), .dout(oprA));
mux8x1 MUX_B (.sel(SB), .d0(reg_out[0]), .d1(reg_out[1]), .d2(reg_out[2]), .d3(reg_out[3]), .d4(reg_out[4]), .d5(reg_out[5]), .d6(reg_out[6]), .d7(reg_out[7]), .dout(oprB));

genvar i;
generate
   for (i=0; i<8; i=i+1) begin : REG  
      reg_16b reg_16b_u (.clk(clk), .start(start), .enb(dec_out[i]), .din(Fout), .dout(reg_out[i]));
   end
endgenerate

endmodule

위에서 만든 세 모듈을 인스턴시에이션하여 register file unit을 완성하였다. r0_reg 는 R0의 출력값을 FND로 확인하고자 포트로 빼준 것이다. 유의할 점은 16비트 reg_out을 8개를 만들어야 하는데 array로 작성하지 않아서 에러가 발생했었다.

구조를 잘 생각하고 문법에 맞게 작성하는 것이 중요하다는 것을 느꼈다. 

 

 

3. Datapath 

3-1

module datapath_unit (clk, start, rw,  sa, sb, dr, fs, pc, mb, md, mm, data_in, vflg, cflg, nflg, zflg, data_out, addr_out, r0_reg);
parameter	BW = 16;
input						clk, start, rw;
//input						ta, tb, td;
input			[2:0]		sa, sb, dr;
input			[4:0]		fs;
input			[7:0]		pc;				//512-words LPM_RAM_DQ 
input						mb, md, mm;
input			[BW-1:0]	data_in;
output	reg					vflg, cflg, nflg, zflg;
output	wire	[BW-1:0]	data_out;
output	wire	[8:0]		addr_out;
output	wire	[BW-1:0]	r0_reg;



wire s_vflg, s_cflg, s_nflg, s_zflg;

wire [BW-1:0] oprA, Fout;
wire [BW-1:0] Reg_oprB, MUXB_out, MUXD_out;


assign MUXB_out = mb ? {{(BW-3){1'b0}},sb} : Reg_oprB;  // Const_in = {{(BW-3){1'b0}},sb}
assign MUXD_out = md ? data_in : Fout;
assign addr_out = mm ?  {1'b0,pc}: oprA[8:0]; //MUXM_out
assign data_out = MUXB_out;
 
Reg_file Reg_u (.clk(clk), .SA(sa), .SB(sb), .DR(dr), .RW(rw), .start(start), .Fout(MUXD_out), .oprB(Reg_oprB), .oprA(oprA), .r0_reg(r0_reg));
Func_unit Func_u (.oprA(oprA), .oprB(MUXB_out), .FS(fs), .SA(sa), .Fout(Fout), .v_flag(v_flag), .c_flag(c_flag), .n_flag(n_flag), .z_flag(z_flag));


always @(posedge clk)
begin
	if (start==1'b0) begin
		vflg <= 1'b0;
		cflg <= 1'b0;
		nflg <= 1'b0;
		zflg <= 1'b0;
	end
	else if (rw) begin
		vflg <= s_vflg;
		cflg <= s_cflg;
		nflg <= s_nflg;
		zflg <= s_zflg;
	end
end


endmodule

마지막으로 register file과 function unit을 연결하고, MUXB, MUXD, MUXM을 assign문으로 적절히 만들어주었다. flag 포트들 앞에 레지스터를 다 달아주었는데, memory에 access할 때 등은 flag값이 바뀌지 않고, fout이 register file로 들어와서

rw할 때만 flag값이 로딩될 수 있도록 하기 위함이다. 레지스터를 달아주지 않으면 예컨데, memory에 값을 저장하기 위한 oprA, B값에 대한 flag도 쓸데없이 계속 로딩되는 것이다. 

 

3-2 시뮬레이션

 

 module tb_datapath_unit;
reg						clk = 1'b1, start, rw;
reg			[2:0]		sa, sb, dr;
reg			[4:0]		fs;
reg			[7:0]		pc;				//512-words LPM_RAM_DQ 
reg						mb, md, mm;
reg			[15:0]	data_in;
wire					vflg, cflg, nflg, zflg;
wire	[15:0]	data_out;
wire	[8:0]		addr_out;
wire	[15:0]	r0_reg;
 
datapath_unit u1 (clk, start, rw,  sa, sb, dr, fs, pc, mb, md, mm, data_in, vflg, cflg, nflg, zflg, data_out, addr_out, r0_reg);

 always #10 clk = ~clk;
 
 initial begin
     start  = 1'b0; rw  = 1'b1;
#20  start = 1'b1;
#20  sb = 3'b001; mb = 1'b1; fs = 5'b10010; md = 1'b0; dr = 3'b001; 
//#20  mb = 1'b0; fs =5'b11100; sa = 2'b010; md = 1'b0; sa = 3'b010; dr = 3'b010;
#40 mb = 1'b0; fs =5'b10100; md = 1'b0; sa = 3'b010; dr = 3'b010;
#40 mm = 1'b0;
#20;  
$stop;
 
 end
 
 endmodule

 

구조가 복잡하여 필요한 변수에만 값을 주어 결과를 확인해보았다. 위의 회로 그림에서 첫 시작은 constant값을 주고 MUXB는 1로 셀렉하여 function unit에서 oprB shift 시키고 register file로 들어와서 oprB에 쉬프트된 값이 제대로 들어오는지 확인한다.

그 다음은 다시 func unit을 거쳐 register file로 들어와서 oprA에 값을 저장하고 확인하는 것이 목적이었다. 그 후  address out이 나오도록 MUXM을 0으로 셀렉하여 oprA값이 잘 나오는지 확인하였다.