Verilog/Verilog 실습

Verilog [CPU 만들기] PC, ALU

오버헤드프레스 2023. 5. 9. 17:01

진행할 과정

program counter = 0부터 시작

PC가 실행되면 BUS로 들어감

MAR 0번지 DATA를 가져가다 ROM에 저장

후에 IR에 들어감

MDR에 데이터나 명령이 들어갈 수 있음 

IR에 명령이 들어가면 control block에 들어감

control block은 디코딩을 함

ex)더하기 3 + 4면 ACC에 3이 ALU를 거쳐 다시 ACC에 들어감. 다음은 ACC를 무시하고 4가 BREG에 들어감. 더한 값은 ALU를 거쳐 ACC에 저장. 또 + 4가 진행되면 7이 저장되어있는 상태에서 BREG에 4가 들어가고 ALU를 통해 ACC에 11이 저장된다.

 

주소를 세어주는 pc만들기
Add Files를 할때

register에 clk이 들어갈 때마다 1씩 증가 >> counter 동작

 

 

Half-adder 8개 만들기

 

`timescale 1ns / 1ps


module hadder_8_bit(
        input A,
        input [7:0] data_in,
        output [7:0] data_out
    );
    
    wire carry_out [7:0];
    
    genvar i;
    generate 
        half_adder ha0(.A(A), .B(data_in[0]), .S(data_out[0]), .C(carry_out[0])); //첫번째 hadder만 그대로 받아야하니까 만들어놓음
        for (i = 1; i < 8; i = i + 1)begin :hagen //콜론하고 이름 = genvar로 만든 모델이름
        half_adder ha(.A(carry_out[i-1]), .B(data_in[i]), .S(data_out[i]), .C(carry_out[i]));            
        end
        
    endgenerate

endmodule

 

half-adder 8개 디자인

 

 

pc코드

`timescale 1ns / 1ps


module program_addr_counter(
    input clk,
    input reset_p,
    input pc_inc,
    input load_pc,
    input pc_o_en,
    input [7:0] pc_in,
    output [7:0] pc_out
    );
    
    wire [7:0] data_out, p_addr,  p_addr_out;
    
    hadder_N_bit #(.N(8)) ha8bit(.A(pc_inc), .data_in(p_addr_out), .data_out(data_out)); //p_addr mux의 출력을 주고있음
    
    assign p_addr = (load_pc) ? pc_in : data_out; //mux는 이렇게 쓰는게 제일 간단함
    
//    genvar i;
//        generate
//        for(i=0;i<8;i=i+1)begin
//                mux_2_1 mux2to1_0(.D({pc_in[i], data_out[i]}), .S(load_pc), .F(p_addr[i]));
//        end
//    endgenerate
    
    register_N_bit_posedge #(.N(8)) sum_reg(
    .D(p_addr),
    .clk(clk),
    .reset_p(reset_p),
    .wr_e_p(1'b1), .rd_e_p(pc_o_en),
    .Q(p_addr_out), //이건 hadder에 넣고 hadder의 data_out은 mux에 넣고 
    .tri_state_Q(pc_out)); //pc_o_en에 0을 주면 임피던스, 1을 주면 값 출력 
    
endmodule

 

counter 코드

`timescale 1ns / 1ps


module hadder_8_bit(
        input A,
        input [7:0] data_in,
        output [7:0] data_out
    );
    
    wire carry_out [7:0];
    
    genvar i;
    generate 
        half_adder ha0(.A(A), .B(data_in[0]), .S(data_out[0]), .C(carry_out[0])); //첫번째 hadder만 그대로 받아야하니까 만들어놓음
        for (i = 1; i < 8; i = i + 1)begin :hagen //콜론하고 이름 = genvar로 만든 모델이름
        half_adder ha(.A(carry_out[i-1]), .B(data_in[i]), .S(data_out[i]), .C(carry_out[i]));            
        end
        
    endgenerate

endmodule


module hadder_N_bit #(parameter N = 8)(
    input A,
    input [N-1:0] data_in,
    output [N-1:0] data_out
    );
    
    wire [N-1:0] carry_out;
    //carry_out을 배열로 만들어 놓으면 파라미터로 가능
    
    genvar i;
    
    generate
            half_adder ha0(.A(A), .B(data_in[0]), .S(data_out[0]), .C(carry_out[0])); //첫번째 hadder만 그대로 받아야하니까 만들어놓음
        for (i=1; i<N; i=i+1)begin
            half_adder ha(.A(carry_out[i-1]), .B(data_in[i]), .S(data_out[i]), .C(carry_out[i]));
        end
    endgenerate
    
endmodule

 

 

테스터 벤치 코드

`timescale 1ns / 1ps


module tb_program_addr_counter();

reg clk, reset_p, pc_inc, load_pc, pc_o_en;
reg [7:0] pc_in;

wire [7:0] pc_out;

program_addr_counter DUT(
 .clk(clk),
 .reset_p(reset_p),
 .pc_inc(pc_inc),
 .load_pc(load_pc),
 .pc_o_en(pc_o_en),
 .pc_in(pc_in),
 .pc_out(pc_out)
    );
    
    initial begin
        clk = 0; reset_p = 1; pc_inc = 0; load_pc = 0; pc_o_en = 1;
        pc_in = 8'b00010011;
    end
    
    always #50 clk = ~clk;
    
    initial begin
        #100;
        reset_p = 0;     #100;
        pc_inc = 1;      #500;
        load_pc = 1;    #100;
        load_pc = 0;    #500;
        pc_o_en = 0;   #100;
        pc_o_en = 1;   #100;
        pc_o_en = 0;   #100;
        reset_p = 1;     #100;
        reset_p = 0;     #100;
        $finish;
    end
endmodule

 

 

시뮬레이션 결과

 

시뮬레이션 결과

pc_inc = 1일 때 clk의 상승에지에서 pc_out의 값은 ++

loda_pc의 값이 1일 때, clk의 상승에지에서 pc_out은 pc_in의 값을 불러온다.

pc_in이 0일 때, pc_out은 임피던스

reset_p가 1일 때 0으로 초기화 = pc_out의 첫 0이 나온 이유는 첫 clk의 상승에지에서 reset_p가 1이기 때문에 0이 된 상태.

 

pc_addr_counter

 

 

zero_flag = 1이면 연산결과가 참, 0이면 거짓

sigh_flag 연산결과가 양이면 1, 음이면 0

carry_flag 올림수가  발생하면 1, 그대로면 0

 

 

가감산기 코드

 

module fadd_subNbit #(parameter N = 4)(
    input [N-1:0] A,
    input [N-1:0] B,
    input Cin,
    output [N-1:0] S,
    output Cout
    );
    
    wire [N:0] carry_out;
    //carry_out을 배열로 만들어 놓으면 파라미터로 가능
    
    assign carry_out[0] = Cin;  
    assign Cout = carry_out[N];  
    
    genvar i;
    
    generate
        for (i=0; i<N; i=i+1)begin
            fadder fa(.A(A[i]), .B(B[i]^Cin), .Cin(carry_out[i]), .S(S[i]), .Cout(carry_out[i+1])); //^Cin 으로 빼기가 가능해짐.
        end
    endgenerate
    
endmodule

 

 

가감산기 테스터벤치 코드

 

`timescale 1ns / 1ps


module tb_fadd_subNbit();

parameter N = 4;

reg [N-1:0] A, B;
reg Cin;
wire [N-1:0]S;
wire Cout;

fadd_subNbit #(.N(N)) DUT(.A(A), .B(B), .Cin(Cin), .S(S), .Cout(Cout));

initial begin
    A = 4'b1010;
    B = 4'b0100;
    Cin = 0;
    #100;
    Cin = 1;
    #100; $finish;
end


endmodule

 

 

가감산기

 

alu 코드

`timescale 1ns / 1ps


module alu(
    input clk, reset_p, op_add, op_sub, op_and, op_mul, op_div, alu_lsb,
    input [3:0] acc_high,
    input [3:0] bus_reg_data,
    output [3:0] alu_out,
    output zero_flag, sign_flag, carry_flag, cout
    );
    
    wire [3:0] sum;
    wire zero_sum;
    
     fadd_subNbit #(.N(4))fadd_sub4bit(.A(acc_high), .B(bus_reg_data), .Cin(op_sub|op_div), .S(sum), .Cout(cout));
    
    assign alu_out = op_and ? (acc_high & bus_reg_data) : sum;
    //op_and가 1일 때라는 뜻,  
    //acc_high & bus_reg_data이게 나옴. 외에는 sum
    //2x1 mux, 조건연산자는 mux가 만들어진다.
    
    register sign_f(.d(!(cout)&op_sub), .clk(clk), .reset_p(reset_p), .in_en(op_sub), .q(sign_flag));
     //결과가 -일 때 carry는 0. 음수가 발생하면 flag
    
    register carry_f(.d((op_add|op_div|(op_mul&alu_lsb))&cout), .clk(clk), .reset_p(reset_p), .in_en(1'b1), .q(carry_flag));
    //alu_lsb는 곱셈할 때 
    
    assign zero_sum = ~(|sum); //zero_sum은 sum이 0일 때 1. sum = 0일 때 or연산이 0이어야 하기때문에 식을 저렇게 함 
    register zero_f(.d(zero_sum), .clk(clk), .reset_p(reset_p), .in_en(op_sub), .q(zero_flag));
    //연산의 결과가 0일때 zero flag 발생

    
endmodule

 

alu 테스터벤치 코드

 

`timescale 1ns / 1ps


module tb_alu();

reg clk, reset_p, op_add, op_sub, op_and, op_mul, op_div, alu_lsb;
reg [3:0] acc_high, bus_reg_data;

wire [3:0] alu_out;
wire zero_flag, sign_flag, carry_flag, cout;
    
alu DUT(
    clk, reset_p, op_add, op_sub, op_and, op_mul, op_div, alu_lsb,
    acc_high,
    bus_reg_data,
    alu_out,
    zero_flag, sign_flag, carry_flag, cout
    );
    
    initial begin
        acc_high = 4'b0101;
        bus_reg_data = 4'b0010;
        op_add = 0;
        op_sub = 0;
        op_mul = 0;
        op_div = 0;
        clk = 0;
        reset_p = 1;
        op_and = 0;
    end
    
    always #50 clk = ~clk;
    
//    initial begin
//        #100;
//        reset_p = 0;#100;
//        op_add = 1; #100;
//        op_add = 0; op_sub = 1; #100;
//        op_sub = 0; op_and = 1; #100;
//        op_and = 0; #100;
//        $finish;
//    end

initial begin
    #100;
    reset_p = 0; #100;
    acc_high = 4'b1101; bus_reg_data = 4'b0111; op_add = 1; #100;
    op_add = 0; acc_high = 4'b0101; bus_reg_data = 4'b1010; op_sub = 1; #100;
    bus_reg_data = 4'b0101; #100;
    op_sub = 0; #100;
    $finish;
end
    
endmodule

alu 테스터벤치 결과

 0100

+     1

---------

0101 = 5

 

 

 

'Verilog > Verilog 실습' 카테고리의 다른 글

Verilog 다목적 시계  (0) 2023.05.16
Verilog HC-SR04 초음파센서 제어  (0) 2023.05.16
Verilog [CPU만들기] ACC  (2) 2023.05.16
Verilog ADC로 전압값을 BCD로 변환  (0) 2023.05.04
Verilog basys3으로 DHT11 제어하기  (0) 2023.05.03