Verilog/Verilog 실습

Verilog [CPU만들기] CPU 제작

오버헤드프레스 2023. 5. 18. 15:20

 

processor_top 모듈

module processor_top(
    input clk, btn,
    input [3:0] col,
    output [3:0] row,
    output [3:0] com_an,
    output [6:0] seg
    );

    wire [3:0] hout, lout, kout, data;
    wire valid;
    
    processor pro(
    .clk(clk), .reset_p(btn),
    .data(data),
    .valid(valid),
    .hout(hout), .lout(lout), .kout(kout)    
    );
    
key_scan ks(
    .clk(clk),
    .reset_n(~btn),
    .col(col), 
    .row(row),
    .key_value(data),
    .valid(valid)
    );    
    
    FND_4digit_switcher fnd( // 1ms에 1번씩 com_an 단자 바꿔줌
    .value_1(kout),
    .value_10(4'b1111),
    .value_100(lout),
    .value_1000(hout),
    .clk(clk),
    .com_an(com_an),
    .seg(seg)
    );
    
endmodule

 

processor

module processor(
    input clk, reset_p,
    input [3:0] data,
    input valid,
    output [3:0] hout, lout, kout    
    );
    wire [7:0] mar_out;
    wire [7:0] rom_out;
    wire rom_en;
    
    rom rom_256 (
  .a(mar_out),              // input wire [7 : 0] a // address 입력
  .qspo_ce(rom_en),  // input wire qspo_ce
  .spo(rom_out)          // output wire [7 : 0] spo
   );
   
    wire inreg_oen;
    wire [7:0] int_bus;
    
    register_N_bit_posedge #(.N(4)) inreg (
        .D(data),
        .clk(clk), .reset_p(reset_p), .wr_e_p(1'b1), .rd_e_p(inreg_oen),
        .tri_state_Q(int_bus[7:4])
    );
    wire [3:0] keych_in;
    assign keych_in = valid ? 4'b1111 : 4'b0000;
    
    wire keych_oen;
    register_N_bit_posedge #(.N(4)) keych_reg(.D(keych_in), .clk(clk), .reset_p(reset_p), .wr_e_p(1'b1), .rd_e_p(keych_oen), .tri_state_Q(int_bus[7:4]));
    
    
    wire mar_inen;
    
    register_N_bit_posedge #(.N(8)) mar (
        .D(int_bus),
        .clk(clk), .reset_p(reset_p), .wr_e_p(mar_inen),
        .Q(mar_out)
    );
   
    wire mdr_inen, mdr_oen;
    
    register_N_bit_posedge #(.N(8)) mdr (
        .D(rom_out),
        .clk(clk), .reset_p(reset_p), .wr_e_p(mdr_inen), .rd_e_p(mdr_oen),
        .tri_state_Q(int_bus)
    );
    
    wire [7:0] ir_out;
    wire ir_inen;
    
    register_N_bit_posedge #(.N(8)) ir (
        .D(int_bus),
        .clk(clk), .reset_p(reset_p), .wr_e_p(ir_inen),
        .Q(ir_out)
    );
    
    wire pc_inc, load_pc, pc_oen;
    
    program_addr_counter pc (
        .clk(clk), .reset_p(reset_p), .pc_inc(pc_inc), .load_pc(load_pc), .pc_o_en(pc_oen),
        .pc_in(int_bus),
        .pc_out(int_bus)
    );
    
    wire breg_inen;
    wire [3:0] bus_reg_in;
    
    register_N_bit_posedge #(.N(4)) breg (
        .D(int_bus[7:4]),
        .clk(clk), .reset_p(reset_p), .wr_e_p(breg_inen),
        .Q(bus_reg_in)
    );
    wire tmpreg_inen, tmpreg_oen;
    
    register_N_bit_posedge #(.N(4)) tmpreg (
        .D(int_bus[7:4]),
        .clk(clk), .reset_p(reset_p), .wr_e_p(tmpreg_inen), .rd_e_p(tmpreg_oen),
        .tri_state_Q(int_bus[7:4])
    );
    
    wire creg_inen, creg_oen;
    
    register_N_bit_posedge #(.N(4)) creg (
        .D(int_bus[7:4]),
        .clk(clk), .reset_p(reset_p), .wr_e_p(creg_inen), .rd_e_p(creg_oen),
        .tri_state_Q(int_bus[7:4])
    );
    wire dreg_inen, dreg_oen;

    register_N_bit_posedge #(.N(4)) dreg (
        .D(int_bus[7:4]),
        .clk(clk), .reset_p(reset_p), .wr_e_p(dreg_inen), .rd_e_p(dreg_oen),
        .tri_state_Q(int_bus[7:4])
    );
    wire rreg_inen, rreg_oen;

    register_N_bit_posedge #(.N(4)) rreg (
        .D(int_bus[7:4]),
        .clk(clk), .reset_p(reset_p), .wr_e_p(rreg_inen), .rd_e_p(rreg_oen),
        .tri_state_Q(int_bus[7:4])
    );
    wire acc_high_reset_p,acc_in_select, acc_o_en;
    wire op_add, op_sub, op_and, op_mul, op_div;
    wire[1:0] acc_low_select, acc_high_select_in;
    wire sign_flag, zero_flag;
    
 block_alu_acc alu_acc(
    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,
    int_bus[7:4], bus_reg_in,
    sign_flag, zero_flag,
    int_bus   //3상 출력
    );
    
    wire outreg_inen;
    wire [7:0] outreg_out;
    
    register_N_bit_posedge #(.N(8)) outreg (
        .D(int_bus),
        .clk(clk), .reset_p(reset_p), .wr_e_p(outreg_inen), .rd_e_p(1'b1),
        .Q(outreg_out)
    );    
    
    wire [15:0] bcd;
    
    assign hout = bcd[7:4];
    assign lout = bcd[3:0];
    
     bin_to_dec b2d(
        .bin({4'b0000, outreg_out}),
        .bcd(bcd)
    );
    
    wire keyout_inen;
    
    register_N_bit_posedge #(.N(4)) keyout (
        .D(int_bus[7:4]),
        .clk(clk), .reset_p(reset_p), .wr_e_p(keyout_inen), .rd_e_p(1'b1),
        .tri_state_Q(kout)
    );
    
    control_block_top control_block(  //control block 이 topmodule
        .clk(clk), .reset_p(reset_p),
        .ir_in(ir_out),  //ir레지스터의 출력 받아오면 됨 
        .zero_flag(zero_flag), .sign_flag(sign_flag),
        .mar_inen(mar_inen), .mdr_inen(mdr_inen), .mdr_oen(mdr_oen), .ir_inen(ir_inen),
        .pc_inc(pc_inc), .load_pc(load_pc), .pc_oen(pc_oen), 
        .breg_inen(breg_inen), .tmpreg_inen(tmpreg_inen), .tmpreg_oen(tmpreg_oen), 
        .creg_inen(creg_inen), .creg_oen(creg_oen), 
        .dreg_inen(dreg_inen), .dreg_oen(dreg_oen), .rreg_inen(rreg_inen), .rreg_oen(rreg_oen), 
        .acc_high_reset_p(acc_high_reset_p), 
        .acc_in_select(acc_in_select), .acc_o_en(acc_o_en), .op_add(op_add), .op_sub(op_sub), 
        .op_and(op_and), .op_mul(op_mul), .op_div(op_div),
        .outreg_inen(outreg_inen), .inreg_oen(inreg_oen), .keych_oen(keych_oen), .keyout_inen(keyout_inen),
        .rom_en(rom_en),
        .acc_low_select(acc_low_select), .acc_high_select_in(acc_high_select_in)
        
    );
endmodule

 

각 레지스터는 register_N_bit_posedge 모듈 사용

 

register_N_bit_posedge 모듈

module register_N_bit_posedge #(parameter N = 8)(//함수 선언, 회로로 만들어주지않음 initial문, parameter문 등
    input [N-1:0] D,
    input clk,
    input reset_p,
    input wr_e_p, rd_e_p,   //1일때는 shift, 0일 때는 load
    output [N-1:0] Q,
    output [N-1:0] tri_state_Q
    );
    
    
    wire [N-1:0] and_o;
    wire [N-1:0] F;
    
    
    genvar i;  //회로 만들때는 무시되고 generate안에서만 사용됨, 아래 generate문 안에 있는 것들은 회로로 만들어줌
    
    generate
        for  (i=0; i<N; i=i+1)begin
            mux_2_1 mx(.D({D[i], Q[i]}), .S(wr_e_p), .F(F[i]));
            and (and_o[i], F[i], ~reset_p);
            D_flip_flop_posedge DFF (.D(and_o[i]), .E(clk), .Q(Q[i]));
            bufif1 (tri_state_Q[i], Q[i],rd_e_p);  //rd_e_n이 0일 때 Q로 나간다. rd_e_p일 때 0이면 Q는 임피던스
        end
        
    endgenerate  //아래것들을 위처럼 간단하게 가능 

    
    endmodule
    
    
    
    
    module register (
    input d, clk, reset_p, in_en,
    output q
    );

    reg q_temp;
    
    always @(posedge clk or posedge reset_p)begin
        if(reset_p) q_temp = 0;
        else if(in_en) q_temp = d;
        else q_temp = q_temp;
    end

    assign q = q_temp;

endmodule

 

block_alu_acc

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 output
    );
    
    assign acc_data = acc_o_en ? {acc_high_data, acc_low_data} : 8'bz;  //tri state buf
    
    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

 

acc와 half acc 코드

odule 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

 

alu 코드

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 = 1일 때 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

 

processor_top 테스터벤치

module tb_processor_top();

reg clk, reset_p;
reg [3:0] data;
reg valid;
wire [3:0] hout, lout, kout;
integer i, j, k;

processor pr(.clk(clk), .reset_p(reset_p), .data(data), .valid(valid), .hout(hout), .lout(lout), .kout(kout));

initial begin
    clk = 0; reset_p = 1; data = 0; valid = 0;
    i = 0; k = 10; j = 0;
    
end

always #50 clk = ~clk;

initial begin
    #100;
    reset_p = 0;
    #100;
    for (k = 10; k < 15; k = k + 1)begin
        for (i = 0; i < 10; i = i + 1)begin
            for (j = 0; j < 10; j = j + 1)begin
                data = i; valid = 1; #300_000 //valid = 1은 키보드 입력이 있다는 뜻 first number 
                valid = 0; #300_000;
                data = k; valid = 1; #300_000;
                valid = 0; #300_000;
                data = j; valid = 1; #300_000;
                valid = 0; #300_000;
                data = 4'b1111; valid = 1; #300_000; //equal을 주려고 F를 함.
                valid = 0; #300_000;
                $display("%d %d %d = %d %d : %d \n", i,  k, j, hout, lout, kout); //print랑 똑같음, 콘솔창에 문자열을 출력해줌 
            end
        end
    end
    $finish;
end

 

D-FlipFlop 을 사용했는데 S-R래치 포함이 아닌 동작적 모델링을 통해 제작해야한다.

FPGA에서는 S-R래치를 만들 수 없음. 회로가 작을 때는 만들어 진다.

 

 

D-FlipFlop 동작적 모델링 코드

module D_flip_flop_posedge(
    input D,
    input E,
    output reg Q
    );
    
    always @(posedge E)begin
        if(E) Q = D;
        else Q = Q;
    end
    
//    wire Ebar, QX;
    
//    not (Ebar, E);
    
//    D_flip_flop DFF1(.D(D), .E(Ebar), .Q(QX));
    
//    D_flip_flop DFF2(.D(QX), .E(E), .Q(Q), .Qbar(Qbar));
    
    
endmodule

 

각 식의 결과

곱셈 결과
나눗셈 결과

15가 나오는건 아무 값도 안들어가서 1111 15출력

 

and연산

헥사값으로 변환후 진행하면 and가 정상적으로 실행

 

뺄셈 결과

11은 음수를 표현하는 것이다.

 

덧셈 결과

시뮬레이션 결과

spo는 ROM에서 나오는 값

Q는 mar의 출력

tri_state_Q는 mdr 출력

 

 

덧셈

 

7 + 7 = 14

 

뺄셈

 

 

7-2 = 5

 

곱셈

 

 7 x 9 = 63

나눗셈

 

8 / 6 = 나머지 2 몫 1

 

Basys3 flash memory에 자동으로 저장되어 전원을 껐다 켜도 실행되게 하기

bin파일 선택

 

add...활성화됨

 

 

 

 

정상적으로 작동하는 것을 확인했다.

cpu전반 알고리즘에 대해 학습할 수 있었다.