Harman 세미콘 아카데미/SoC를 위한 Peripheral 설계

2024.11.04 [SoC를 위한 Peripheral 설계]9 - cpu의 구조와 동작원리3(Program Counter, Processor)

U_Pong 2024. 11. 4. 15:45

2024.11.04 수업날


< Program Counter >

PC의 최종 블록 다이어그램

 

 

Vivado 진행 순서

 

 

half_adder_N_bit
/////////////////////////////////////////////////// 2024.11.04
module half_adder_N_bit #(parameter N = 8)(
      input inc,
      input [N-1:0] load_data,
      output [N-1:0] sum);

     wire [N-1:0] carry_out;
     
     half_adder_dataflow ha0(.a(inc), .b(load_data[0]), .s(sum[0]), .c(carry_out[0]));
     
     genvar i;                //genvar는 generate에서 만든 변수
     generate
            for(i=1; i<N; i=i+1) begin:hagen
                  half_adder_dataflow ha(.a(carry_out[i-1]), .b(load_data[i]), .s(sum[i]), .c(carry_out[i]));
            end
     endgenerate
     
//     half_adder_dataflow ha1(.a(carry_out[0]), .b(load_data[1]), .s(sum[1]), .c(carry_out[1]));
//     half_adder_dataflow ha2(.a(carry_out[1]), .b(load_data[2]), .s(sum[2]), .c(carry_out[2]));
//     half_adder_dataflow ha3(.a(carry_out[2]), .b(load_data[3]), .s(sum[3]), .c(carry_out[3]));
//     half_adder_dataflow ha4(.a(carry_out[3]), .b(load_data[4]), .s(sum[4]), .c(carry_out[4]));
      
endmodule

 

generate문을 쓰면 왼쪽에 N을 설정한 만큼 추가되는 것을 확인할 수 있다.
추가되는 것의 이름을 바꾸고 싶다면 begin 다음에 ':이름' 이런식으로 쓰면 된다.

 

 

Program Counter
/////////////////////////////////////////////////////////////
module Program_addr_counter(
      input clk, reset_p,
      input pc_inc, load_pc, pc_rd_en,          // pc_inc 1씩 증가, pc_rd_en은 pc_out이 어떤 출력으로 될 것인지를 정함
      input [7:0] pc_in,
      output [7:0] pc_out);
      
      wire [7:0] sum, cur_addr, next_addr;
      
      assign next_addr = load_pc ? pc_in : sum;
      
      half_adder_N_bit #(.N(8)) pc(
            .inc(pc_inc),
            .load_data(cur_addr),
            .sum(sum));
      
      register_Nbit_p # (.N(8)) pc_reg(         
            .clk(clk), .reset_p(reset_p),
            .d(next_addr),
            .wr_en(1), .rd_en(pc_rd_en),     
            .register_data(cur_addr),
            .q(pc_out));
      
endmodule

 

 

pc 시뮬레이션 결과는 아래와 같다.
pc_inc 에 1을 줬을 때는 1이 더해져야하고,
load_pc 에 1을 줬을 때는 값이 그대로 load,
pc_rd_en에 1을 줬을 때는 값이 출력되어야 한다.
pc_inc 에 1을 줄때는 load_pc 에 0을 줘야 값 확인할 수 있다.

 

 

 

< Processor >

Vivado 진행 순서

 

 

Processor
///////////////////////////////////////////////// 2024.11.04
module Processor(
      input clk, reset_p,
      input [3:0] key_value,
      input key_valid,
      output [3:0] kout,                        // cpu가 key 입력을 받았는지 확인함
      output [7:0] outreg_data);
      
      wire [7:0] int_bus, mar_out, rom_out;
      wire mar_inen, mdr_inen;                      // in enable
      wire mdr_oen;                                         // out enable
      wire pc_inc, load_pc, pc_oen;
      wire [7:0] ir_data;
      wire ir_inen;
      wire [3:0] bus_reg_data;
      wire breg_inen;
      wire acc_high_reset_p, acc_oen, acc_in_select; 
      wire [1:0] acc_high_select_in, acc_low_select;
      wire op_add, op_sub, op_mul, op_div, op_and;
      wire zero_flag, sign_flag;
      wire tmpreg_inen, tmpreg_oen;
      wire creg_inen, creg_oen;
      wire dreg_inen, dreg_oen;
      wire rreg_inen, rreg_oen;
      wire outreg_inen;
      wire keychreg_oen;
      wire inreg_oen;
      wire keyout_inen;
      
      
      // Control_Block 모듈: CPU 제어 논리
      // 입력: 클럭, 리셋, IR 데이터, 플래그
      // 출력: 여러 컨트롤 신호 (MAR, MDR, PC, ALU 등) 활성화 및 설정 신호
      Control_Block CB(
            clk, reset_p,
            ir_data,
            zero_flag, sign_flag,
            mar_inen, mdr_inen, mdr_oen, ir_inen, pc_inc, load_pc, pc_oen, breg_inen,
            acc_high_reset_p, acc_oen, acc_in_select,
            acc_high_select_in, acc_low_select,
            op_add, op_sub, op_mul, op_div, op_and,
            tmpreg_inen, tmpreg_oen, creg_inen, creg_oen, 
            dreg_inen, dreg_oen, rreg_inen, rreg_oen, 
            outreg_inen, keychreg_oen, inreg_oen, keyout_inen, rom_en);
      
      
      // Program_addr_counter 모듈: Program Counter 제어
      // PC 증가 또는 로드 수행, 내부 버스를 통해 값을 전달
      Program_addr_counter PC(
            .clk(clk), .reset_p(reset_p),
            .pc_inc(pc_inc), .load_pc(load_pc), .pc_rd_en(pc_oen),         
            .pc_in(int_bus),
            .pc_out(int_bus));
      
      
      // MAR (Memory Address Register): 메모리 주소 저장
      register_Nbit_p # (.N(8)) MAR(         
            .clk(clk), .reset_p(reset_p),
            .d(int_bus),                                          // 내부 버스에서 입력 받음
            .wr_en(mar_inen), .rd_en(1),     
            .register_data(mar_out));                 // 출력 데이터 (MAR의 주소 값)
      
      
      // MDR (Memory Data Register): 메모리 데이터 저장
      register_Nbit_p # (.N(8)) MDR(         
            .clk(clk), .reset_p(reset_p),
            .d(rom_out),                                                      // ROM의 출력 데이터를 입력
            .wr_en(mdr_inen), .rd_en(mdr_oen),     
            .q(int_bus));                                                     // 내부 버스에 출력 연결
      
      
      // IR (Instruction Register): 명령어 저장
      register_Nbit_p # (.N(8)) IR(         
            .clk(clk), .reset_p(reset_p),
            .d(int_bus),                                                      // 내부 버스에서 입력 받음
            .wr_en(ir_inen), .rd_en(1),     
            .register_data(ir_data));                                 // 출력 데이터 (명령어)
      
      
      // BREG: ALU 연산을 위해 데이터 보관 (4비트)
      register_Nbit_p # (.N(4)) BREG(                 // ALU를 4 bit로 사용했기 때문
            .clk(clk), .reset_p(reset_p),
            .d(int_bus[7:4]),                                            // 상위 4비트만 입력으로 사용
            .wr_en(breg_inen), .rd_en(1),     
            .register_data(bus_reg_data));                  // ALU로 전달할 데이터
      
      
      // ALU 및 ACC: 산술 및 논리 연산 수행
      Block_ALU_ACC alu_acc(
            .clk(clk), .reset_p(reset_p), .acc_high_reset_p(acc_high_reset_p),
            .rd_en(acc_oen), .acc_in_select(acc_in_select),
            .acc_high_select_in(acc_high_select_in), .acc_low_select(acc_low_select),
            .bus_data(int_bus[7:4]),                                    // ALU 입력 데이터
            
            .op_add(op_add), .op_sub(op_sub), .op_mul(op_mul), .op_div(op_div), .op_and(op_and),      // 연산 제어
            .bus_reg_data(bus_reg_data),                          // 레지스터 데이터
            .zero_flag(zero_flag), .sign_flag(sign_flag),    // 상태 플래그
            
            .acc_data(int_bus));                                        // 내부 버스로 결과 출력
      
      
      // TMPREG: 임시 레지스터
      register_Nbit_p # (.N(4)) TMPREG(                 
            .clk(clk), .reset_p(reset_p),
            .d(int_bus[7:4]),
            .wr_en(tmpreg_inen), .rd_en(tmpreg_oen),     
            .q(int_bus[7:4]));
      
      
      // CREG, DREG, RREG: 추가 레지스터 (4비트)
      register_Nbit_p # (.N(4)) CREG(                 
            .clk(clk), .reset_p(reset_p),
            .d(int_bus[7:4]),
            .wr_en(creg_inen), .rd_en(creg_oen),     
            .q(int_bus[7:4]));
      
      
      register_Nbit_p # (.N(4)) DREG(                 
            .clk(clk), .reset_p(reset_p),
            .d(int_bus[7:4]),
            .wr_en(dreg_inen), .rd_en(dreg_oen),     
            .q(int_bus[7:4]));
      
      
      register_Nbit_p # (.N(4)) RREG(                 
            .clk(clk), .reset_p(reset_p),
            .d(int_bus[7:4]),
            .wr_en(rreg_inen), .rd_en(rreg_oen),     
            .q(int_bus[7:4]));
      
      
      // OUTREG: CPU 출력용 레지스터
      register_Nbit_p # (.N(8)) OUTREG(                 
            .clk(clk), .reset_p(reset_p),
            .d(int_bus),
            .wr_en(outreg_inen),     
            .register_data(outreg_data));
      
      
      // KEYCHREG: 키 입력 유효성을 확인하기 위한 레지스터
      register_Nbit_p # (.N(4)) KEYCHREG(                 
            .clk(clk), .reset_p(reset_p),
            .d({key_valid, key_valid, key_valid, key_valid}),
            .wr_en(1), .rd_en(keychreg_oen),     
            .q(int_bus[7:4]));
      
      // INREG: 키 값을 저장하는 레지스터
      register_Nbit_p # (.N(4)) INREG(                 
            .clk(clk), .reset_p(reset_p),
            .d(key_value),
            .wr_en(1), .rd_en(inreg_oen),     
            .q(int_bus[7:4]));
      
      // KEYOUTREG: 키 출력 확인 레지스터
      register_Nbit_p # (.N(4)) KEYOUTREG(                 
            .clk(clk), .reset_p(reset_p),
            .d(int_bus[7:4]),
            .wr_en(keyout_inen), .rd_en(1),     
            .register_data(kout));
      
endmodule

 

 

 

Processor에 인스턴스 하기 위해 Control Block 모듈을 추가한다.

 

 

Control_Block
///////////////////////////////////////// 2024.11.04
module Control_Block(
            input clk, reset_p,
            input [7:0] ir_data,
            input zero_flag, sign_flag,
            output mar_inen, mdr_inen, mdr_oen, ir_inen, pc_inc, load_pc, pc_oen, breg_inen,
            output [1:0] acc_high_reset_p, acc_oen, acc_in_select,
            output [1:0] acc_high_select_in, acc_low_select,
            output op_add, op_sub, op_mul, op_div, op_and,
            output tmpreg_inen, tmpreg_oen, creg_inen, creg_oen, 
            output dreg_inen, dreg_oen, rreg_inen, rreg_oen, 
            output outreg_inen, keychreg_oen, inreg_oen, keyout_inen, rom_en);
      
      
endmodule

cpu의 구조와 동작원리3(Program Counter, Processor) 끝!