Verilog/Verilog 실습

Verilog [CPU만들기] ACC

오버헤드프레스 2023. 5. 16. 17:29

ACC 코드

 

`timescale 1ns / 1ps


module acc(
    input clk, reset_p, acc_high_reset_p,acc_in_select,fill_value,
    input [1:0] acc_high_select, acc_low_select,
    input [3:0] aludata, bus_in,
    output [3:0] acc_high_data,
    output [3:0] acc_low_data
    );
    
    wire [3:0] acc_high_in;
    
    assign acc_high_in = acc_in_select ? bus_in : aludata; 
    
    half_acc acc_high(  
    //high만 reset해줄 필요가 있음. 상위4bit를 넘기고 reset해줘야함. 하위 4bit를 넘기는애는 어차피 넘겨서 안해도 됨
        .clk(clk), .reset_p(reset_p | acc_high_reset_p), .load_msb(fill_value), .load_lsb(acc_low_data[3]), //load_msb 최상위 비트에 들어가는것 
        .S(acc_high_select), //S입력이 0 0일 때는 아무것도 안함. 0 1일 때는 shift right, 10일 때는 shift left, 11일 때는 load(외부에서 4bit 받을 거)
        .data_in(acc_high_in), //입력을 bus로 받을건지, alu로 받을건지 결정 가능해짐
        .data(acc_high_data));
        
        half_acc acc_low(
    //high만 reset해줄 필요가 있음. 상위4bit를 넘기고 reset해줘야함. 하위 4bit를 넘기는애는 어차피 넘겨서 안해도 됨
        .clk(clk), .reset_p(reset_p), .load_msb(acc_high_data[0]), .load_lsb(fill_value), //load_msb 최상위 비트에 들어가는것 
        .S(acc_low_select), //S입력이 0 0일 때는 아무것도 안함. 0 1일 때는 shift right, 10일 때는 shift left, 11일 때는 load(외부에서 4bit 받을 거)
        .data_in(acc_high_data),
        .data(acc_low_data));
    
    
endmodule

module half_acc (
        input clk, reset_p, load_msb, load_lsb,
         //load_msb 최상위 비트에 들어가는것, load_msb는 하위 4bit로 미는데, 하위 4bit는 shift_right하고 상위 4bit를 따로 채우면 됨
        input [1:0] S,
         //S입력이 0 0일 때는 아무것도 안함. 0 1일 때는 shift right, 10일 때는 shift left, 11일 때는 load(외부에서 4bit 받을 거)
        input [3:0] data_in,
        output [3:0] data
);

wire [3:0] D;

mux_4_1 mux3(.D({data_in[3], data[2], load_msb, data[3]}), .S(S), .F(D[3])); //순서: load > left > right > pass
mux_4_1 mux2(.D({data_in[2], data[1], data[3], data[2]}), .S(S), .F(D[2]));    //D(2번bit, 1번 bit, 0번 bit, 번 bit)
mux_4_1 mux1(.D({data_in[1], data[0], data[2], data[1]}), .S(S), .F(D[1]));    //최상위 비트를 load_msb입력 받을 때는 다른bit로 채움
mux_4_1 mux0(.D({data_in[0], load_lsb, data[1], data[0]}), .S(S), .F(D[0]));

register_N_bit_posedge #(.N(4))reg_acc_4bit(.D(D), .clk(clk), .reset_p(reset_p), .wr_e_p(1'b1), .rd_e_p(1'b1), .Q(data));
//tri_state_Q 는 rd_e_p가 0일 때 임피던스 나옴, 1이면 1출력
endmodule

 

ACC 테스터 벤치 코드

 

`timescale 1ns / 1ps


module tb_acc();

reg clk, reset_p, acc_high_reset_p, acc_in_select, fill_value;
reg [1:0] acc_high_select, acc_low_select;
reg [3:0] aludata, bus_in;

wire [3:0] acc_high_data, acc_low_data;

acc DUT(
    clk, reset_p, acc_high_reset_p,acc_in_select,fill_value,
    acc_high_select, acc_low_select,
    aludata, bus_in,
    acc_high_data,
    acc_low_data
    );
    
    initial begin
        clk = 0;  reset_p = 1; acc_high_reset_p = 1; acc_in_select = 0; fill_value = 0;
        acc_high_select = 0; acc_low_select = 0;
        aludata = 4'b0010;
        bus_in = 4'b0101;
    end
    
    always #50 clk = ~clk;
    
    initial begin
        #100;
        reset_p = 0; acc_high_reset_p = 0;                            #100;
        acc_high_select = 2'b11;                                        #100;
        acc_high_select = 2'b00;                                        #100;
        acc_in_select = 1; acc_high_select = 2'b11;               #100;
        acc_high_select = 2'b00; acc_low_select = 2'b11;       #100;
        acc_low_select = 2'b00;                                         #100;
        acc_high_select = 2'b01; acc_low_select = 2'b01;       #100;
        acc_high_select = 2'b00; acc_low_select = 2'b00;       #100;
        acc_high_select = 2'b10; acc_low_select = 2'b10;       #100;
        acc_high_select = 2'b00; acc_low_select = 2'b00;       #100;
        acc_high_select = 2'b10; acc_low_select = 2'b01;       #100;
        acc_high_select = 2'b00; acc_low_select = 2'b00;       #100;
        acc_high_reset_p = 1;                                            #100;
        acc_high_reset_p = 0;                                            #100;
        $finish;
    end 
endmodule

 

 

 

곱셈 방법

 

 

곱셈 코드

 

`timescale 1ns / 1ps

module block_alu_acc(
    input clk, reset_p, acc_high_reset_p,acc_in_select, acc_o_en,
    input op_add, op_sub, op_and, op_mul, op_div,
    input [1:0] acc_low_select, acc_high_select_in,
    input [3:0] bus_in, bus_reg_in,
    output sign_flag, zero_flag,
    output [7:0] acc_data
    );
    
    wire carry_flag, cout;
    wire [1:0] acc_high_select;
    wire [3:0] aludata, acc_high_data, acc_low_data;
    
    assign acc_high_select[1] = (op_mul|op_div) ? (op_mul&acc_low_data[0]) | (op_div&cout) : acc_high_select_in[1];
    assign acc_high_select[0] = (op_mul|op_div) ? (op_mul&acc_low_data[0]) | (op_div&cout) : acc_high_select_in[0];
    
   //(op_mul|op_div) 곱셈을 할거라 | 사용. (op_mul&acc_low_data[0]),(op_mul&acc_low_data[0]) = 00 또는 11밖에 안나옴
   //op_mul일 때도 더하는데 acc_low_data가 0이면 안나오고 1이면 나올것. 
   //뺀결과가 0이면 양수, 뺀 결과가 1이면 음수(뺄 수 있으면 cout = 1), (op_div&cout)도 00 또는 11만 나옴
   
    acc block_acc(
    clk, reset_p, acc_high_reset_p,acc_in_select, carry_flag,    //1bit input  
    acc_high_select, acc_low_select,                                 //2bit input
    aludata, bus_in,                                                        //4bit input
    acc_high_data, acc_low_data                                       //4bit input
    );
    
    assign acc_data = acc_o_en ? {acc_high_data, acc_low_data} : 8'bz;
    
    alu block_alu(
    clk, reset_p, 
    op_add, op_sub, op_and, op_mul, op_div, acc_low_data[0],
    acc_high_data,      //<<acc_high
    bus_reg_in,           //<< bus_reg_data                         
    aludata,                //<<alu_out
    zero_flag, sign_flag, carry_flag, cout
    );
    
    
    
endmodule

 

assign acc_high_select[1] = (op_mul|op_div) ? (op_mul&acc_low_data[0]) | (op_div&cout) : acc_high_select_in[1];
assign acc_high_select[0] = (op_mul|op_div) ? (op_mul&acc_low_data[0]) | (op_div&cout) : acc_high_select_in[0];

 

(op_mul|op_div) 둘다 1이 나올 수 없기때문에 op_mul이 1이면 acc_low_data 를 보고

op_div가 1이면 cout을 보면 된다.

 

 

곱셈 테스터 벤치 코드

 

`timescale 1ns / 1ps

module tb_block_alu_acc( );

reg clk, reset_p, acc_high_reset_p,acc_in_select, acc_o_en;
reg op_add, op_sub, op_and, op_mul, op_div;
reg [1:0] acc_low_select, acc_high_select_in;
reg [3:0] bus_in, bus_reg_in;
wire sign_flag, zero_flag;
wire [7:0]  acc_data;

block_alu_acc DUT(
    clk, reset_p, acc_high_reset_p,acc_in_select, acc_o_en,
    op_add, op_sub, op_and, op_mul, op_div,
    acc_low_select, acc_high_select_in,
    bus_in, bus_reg_in,
    sign_flag, zero_flag,
    acc_data
    );
    
  
    
    initial begin
        clk = 0;  reset_p = 1; acc_high_reset_p = 1; acc_in_select = 0; acc_o_en = 1;
        op_add = 0; op_sub = 0; op_and = 0; op_mul = 0; op_div = 0;
        acc_high_select_in = 0; acc_low_select = 0;
        bus_reg_in = 4'b0101;
        bus_in = 4'b0011;
    end
    
    always #50 clk = ~clk;
    
    initial begin
        #100;
        reset_p = 0; acc_high_reset_p = 0;                            #100;
        acc_in_select = 1; acc_high_select_in = 2'b11;            #100;
        acc_in_select = 0; acc_high_select_in = 2'b00;            #100;
        acc_low_select = 2'b11;                                         #100;
        acc_low_select = 2'b00;                                         #100;
        acc_high_reset_p = 1;                                            #100;
        acc_high_reset_p = 0;                                            #100;
        op_mul = 1;                    #100;   //clk1
        acc_high_select_in = 2'b01; acc_low_select = 2'b01;   op_mul =0; #100;//clk2
        acc_low_select = 2'b00;   op_mul =1; #100;  //clk3
        acc_high_select_in = 2'b01; acc_low_select = 2'b01;   op_mul =0; #100;//clk4
        acc_low_select = 2'b00;   op_mul =1; #100;  //clk5
        acc_high_select_in = 2'b01; acc_low_select = 2'b01;   op_mul =0; #100; //clk6
        acc_low_select = 2'b00;   op_mul =1; #100;  //clk7
        acc_high_select_in = 2'b01; acc_low_select = 2'b01;   op_mul =0; #100; //clk8
        acc_high_select_in = 2'b00; acc_low_select = 2'b00;                  #100;  //clk9
        $finish;
    end 
    
endmodule

테스터 벤치  설명

op_mul = 1;                    #100;   //clk1
        acc_high_select_in = 2'b01; acc_low_select = 2'b01;   op_mul =0; #100;//clk2
        acc_low_select = 2'b00;   op_mul =1; #100;  //clk3
        acc_high_select_in = 2'b01; acc_low_select = 2'b01;   op_mul =0; #100;//clk4
        acc_low_select = 2'b00;   op_mul =1; #100;  //clk5
        acc_high_select_in = 2'b01; acc_low_select = 2'b01;   op_mul =0; #100; //clk6
        acc_low_select = 2'b00;   op_mul =1; #100;  //clk7
        acc_high_select_in = 2'b01; acc_low_select = 2'b01;   op_mul =0; #100; //clk8
        acc_high_select_in = 2'b00; acc_low_select = 2'b00;                  #100;  //clk9
        $finish;

op_mul = 1일 때는 acc_high_select_in을 빼도 됨.

 

op_mul = 1이면 앞 코드

 

assign acc_high_select[1] = (op_mul|op_div) ? (op_mul&acc_low_data[0]) | (op_div&cout) : acc_high_select_in[1];
assign acc_high_select[0] = (op_mul|op_div) ? (op_mul&acc_low_data[0]) | (op_div&cout) : acc_high_select_in[0];

 

이 부분에 의해 어차피 acc_high_select = 2'b11

 

 

곱셈 시뮬레이션

 

 

 

 

곱셈과 나눗셈은 코드가 동일하고 테스터 벤치만 바꿨다.

 

 

나눗셈 테스터 벤치 코드

 

`timescale 1ns / 1ps


module tb_block_alu_acc( );

reg clk, reset_p, acc_high_reset_p,acc_in_select, acc_o_en;
reg op_add, op_sub, op_and, op_mul, op_div;
reg [1:0] acc_low_select, acc_high_select_in;
reg [3:0] bus_in, bus_reg_in;
wire sign_flag, zero_flag;
wire [7:0]  acc_data;

block_alu_acc DUT(
    clk, reset_p, acc_high_reset_p,acc_in_select, acc_o_en,
    op_add, op_sub, op_and, op_mul, op_div,
    acc_low_select, acc_high_select_in,
    bus_in, bus_reg_in,
    sign_flag, zero_flag,
    acc_data
    );
    
  
    
    initial begin
        clk = 0;  reset_p = 1; acc_high_reset_p = 1; acc_in_select = 0; acc_o_en = 1;
        op_add = 0; op_sub = 0; op_and = 0; op_mul = 0; op_div = 0;
        acc_high_select_in = 0; acc_low_select = 0;      
        bus_in = 4'b0111;
        bus_reg_in = 4'b0010;
    end
    
    always #50 clk = ~clk;
    
    initial begin
        #100;
        reset_p = 0; acc_high_reset_p = 0;              
        acc_in_select = 1; acc_high_select_in = 2'b11;            #100; //clk1
        acc_in_select = 0; acc_high_select_in = 2'b00; 
        acc_low_select = 2'b11;                                         #100;  //clk2
        acc_low_select = 2'b00;  
        acc_high_reset_p = 1;                                            #100;  //clk3
        acc_high_reset_p = 0; 
        acc_low_select = 2'b10; acc_high_select_in = 2'b10;  #100;
        acc_low_select = 2'b00; op_div = 1; #100;
        acc_low_select = 2'b10; op_div = 0; acc_high_select_in = 2'b10;  #100;
        acc_low_select = 2'b00; op_div = 1; #100;
        acc_low_select = 2'b10; op_div = 0; acc_high_select_in = 2'b10;  #100;
        acc_low_select = 2'b00; op_div = 1; #100;
        acc_low_select = 2'b10; op_div = 0; acc_high_select_in = 2'b10;  #100; // 맨 마지막에는 하위비트만 밀기 
        acc_low_select = 2'b00; op_div = 1; #100;
        acc_low_select = 2'b10; op_div = 0; acc_high_select_in = 2'b00; #100; 
        acc_low_select = 2'b00; #100;
        $finish;
    end
endmodule

 

 

나눗셈 시뮬레이션