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

2024.10.14 [SoC를 위한 Peripheral 설계]7 - cpu의 구조와 동작원리1(ALU, ACC)

U_Pong 2024. 10. 14. 23:32

2024.10.14 수업날


< cpu의 구조와 동작원리 >

CPU_.pdf
0.02MB

 

 

ALU와 ACC의 구조 및 동작 원리

 

 

vivado 진행 순서

새로운 project 만들어서 RTL project로 설정한 후, basys3 보드로 설정한다.

 

Create File에서 ALU, ACC 생성

 

Add Files에서 아래와 같이 추가

 

 

ALU 소스코드
////////////////////////////////////////////////////// 2024.10.14
module ALU(
      input clk, reset_p,
      input op_add, op_sub, op_mul, op_div, op_and,
      input alu_lsb,                                                                // 연산 결과의 최하위 비트를 보기 위함
      input [3:0] acc_high_data, bus_reg_data,
      output [3:0] alu_data,
      output carry_flag, zero_flag, sign_flag, c_out);
    
      wire [3:0] sum;
      fadd_sub_8bit_dataflow fadd_sub(
            .a(acc_high_data), .b(bus_reg_data), 
            .s(op_sub | op_div),                            // 나누기 할 때도 뺄셈을 하도록
            .sum(sum), 
            .carry(c_out));    
      
      assign   alu_data = op_and ? (acc_high_data & bus_reg_data) : sum;
      
      register_Nbit_p # (.N(1)) zero_f(
            .d(~(|sum)),                              // | 앞에 아무것도 없음 -> 4비트를 각각 or 연산 , (!sum)으로 써도 됨
            .clk(clk),
            .reset_p(reset_p),
            .wr_en(op_sub), .rd_en(1),     //WR Enable, Read Enable
            .q(zero_flag));                           // 뺄셈의 결과가 0일 때만 zero_flag가 1
      
       register_Nbit_p # (.N(1)) sign_f(     // 빼기 했을 때만->값이 음수가 될때만 sign_flag가 1이 됨 
            .d(~c_out & (op_sub)),   
            .clk(clk),
            .reset_p(reset_p),
            .wr_en(op_sub), .rd_en(1),  
            .q(sign_flag));    
      
       register_Nbit_p # (.N(1)) carry_f(          
            .d(c_out & (op_add | (op_mul & alu_lsb) | op_div)), 
            .clk(clk),
            .reset_p(reset_p),
            .wr_en(1), .rd_en(1),              
            .q(carry_flag));                          // 덧셈, 곱셈 했을 때 올림수 확인
      
endmodule

 

 

시뮬레이션 결과를 확인하기 전에 verilog 수업시간에 만들었던 파일에서 아래와 같이 코드를 추가해준다.

 

 

시뮬레이션 결과를 확인하기 위해 clk 10ns, reset 1ns, add 0, op는 다 0으로 설정해서 초기설정을 맞춘다.

아래의 시뮬레이션에는 op_add(덧셈), op_sub(뺄셈) 결과를 확인할 수 있다.

 

시뮬레이션 결과에서  op_sub(뺄셈)에서의 의미는 다음과 같다.

1. c_out이 1이면 a가 b보다 크거나 a와 b가 같고, c_out이 0이면 a가 b보다 작다.

2. zero_flag, sign_flag(결과값이 음수일때 1)가 한 클록 늦게 1이 되는 이유는 상승 엣지에 맞춰서 결과값이 나타나는 것이기 때문이다.
3. zero_flag, sign_flag는 op_sub(뺄셈)일때만 의미가 유효하다. 때문에 나머지 연산에서는 안봐도 된다.

 

 

아래는 op_and를 활성화 시켰을 때의 결과이다.

acc_high_data[3:0], bus_reg_data[3:0] 에서 4,3 입력값을 줬을 때와 4,7 입력값을 줬을 때의 결과이다.

여기서 op_and일 때 zero_flag가 1인 이유는 op_sub에서의 결과값이 0이기 때문에 그 값이 유지되서 계속 1인 것이다. 따라서 zero_flag는 신경 안써도 된다.

 

 

 

다음으로 shift 연산을 확인하기 위해 ACC에서 소스코드를 작성한다.

ACC 소스코드
///////////////////////////////////////////////// 2024.10.14
/////////////////////////////////////// shift 연산은 acc에서 함
module ACC(
      input clk, reset_p, acc_high_reset_p,                 // acc_high_reset_p는 상위 비트만 리셋
      input fill_value, rd_en, acc_in_select,
      input [1:0] acc_high_select, acc_low_select,
      input [3:0] bus_data, alu_data,
      output [3:0] high_data2bus, acc_high_data2alu,
      output [3:0] low_data2bus, acc_low_data);
      
      wire [3:0] acc_high_in;
      assign acc_high_in = acc_in_select ? bus_data : alu_data;
      
      half_acc acc_high(                        // 상위 4비트
            .clk(clk), .reset_p(reset_p | acc_high_reset_p),
            .load_msb(fill_value), .load_lsb(acc_low_data[3]),
            .rd_en(rd_en),
            .s(acc_high_select),
            .data_in(acc_high_in),
            .data2bus(high_data2bus), .register_data(acc_high_data2alu));
      
      
      half_acc acc_low(                         // 하위 4비트
            .clk(clk), .reset_p(reset_p),
            .load_msb(acc_high_data2alu[0]), .load_lsb(fill_value),       
            .rd_en(rd_en),
            .s(acc_low_select),
            .data_in(acc_high_data2alu),
            .data2bus(low_data2bus), .register_data(acc_low_data));
      
endmodule


/////////////////////////////////////////////////////////
module half_acc(
      input clk, reset_p,
      input load_msb, load_lsb,           // msb: 최상위 비트, lsb: 최하위 비트
      input rd_en,                  // 1 줬을 때만 bus로부터 데이터를 받음
      input [1:0] s,
      input [3:0] data_in,
      output [3:0] data2bus, register_data);
      
      parameter IDLE = 2'b00;
      parameter SHIFT_RIGHT = 2'b01;
      parameter SHIFT_LEFT = 2'b10;
      parameter LOAD = 2'b11;
      
      reg [3:0] d;
      always @* begin
            case(s)
                  IDLE: d = register_data;                                                       // 현재값 유지
                  SHIFT_RIGHT: d = {load_msb, register_data[3:1]};         // 우shift
                  SHIFT_LEFT: d = {register_data[2:0], load_lsb};             // 좌shift
                  LOAD: d = data_in;                                                            // bus 데이터를 받으면 데이터 load
            endcase
      end
      
      register_Nbit_p # (.N(4)) half_acc(                         // acc에서는 상위 4비트, 하위 4비트를 사용하기 때문    
            .clk(clk),
            .reset_p(reset_p),   
            .d(d), 
            .wr_en(1), .rd_en(rd_en),      
            .register_data(register_data),        
            .q(data2bus)); 
      
endmodule

 

 

이제 ACC의 testbench를 위해 simulation sources를 추가로 생성한다.

 

 

testbench_acc 소스코드(우 shift, 좌 shift 확인)
//////////////////////////////////////////////// 2024.10.14
module tb_acc();

      parameter IDLE = 2'b00;
      parameter SHIFT_RIGHT = 2'b01;
      parameter SHIFT_LEFT = 2'b10;
      parameter LOAD = 2'b11;
      parameter DATA_ALU = 0;
      parameter DATA_BUS = 1;
      
      // 원래 코드에서의 input -> reg  ,  output -> wire로 설정
      reg clk, reset_p, acc_high_reset_p;
      reg fill_value, rd_en, acc_in_select;
      reg [1:0] acc_high_select, acc_low_select;
      reg [3:0] bus_data, alu_data;
      wire [3:0] high_data2bus, acc_high_data2alu;
      wire [3:0] low_data2bus, acc_low_data;
      
      ACC DUT(
            clk, reset_p, acc_high_reset_p,
            fill_value, rd_en, acc_in_select,
            acc_high_select, acc_low_select,
            bus_data, alu_data,
            high_data2bus, acc_high_data2alu,
            low_data2bus, acc_low_data);
      
      // 초기화 설정
      initial begin
            clk = 0; reset_p = 1; acc_high_reset_p = 0;
            fill_value = 0;
            rd_en = 1;
            acc_in_select = 0;
            acc_high_select = 0;
            acc_low_select = 0;
            bus_data = 4'b0010;
            alu_data = 4'b0101;
      end
      
      // 클록 설정
      always #5 clk = ~clk;
      
      // 결과값 확인
      initial begin
            #10;
            reset_p = 0; #10;
            acc_high_select = LOAD; #10;             //alu 데이터 로드
            acc_high_select = IDLE; #10;
            acc_in_select = DATA_BUS; acc_high_select = LOAD; #10;            // bus 데이터 로드
            acc_high_select = IDLE; acc_low_select = LOAD; #10;        // 상위 비트 데이터 로드
            acc_low_select = IDLE; #10;
            acc_in_select = DATA_ALU; acc_high_select = LOAD; #10;
            acc_high_select = SHIFT_RIGHT; acc_low_select = SHIFT_RIGHT; #40;             // 우shift
            
            acc_in_select = DATA_BUS; acc_high_select = LOAD; #10;            //bus 데이터를 상위 비트 데이터에 load
            acc_high_select = IDLE; acc_low_select = LOAD; #10;        // 상위 비트 데이터 로드
            acc_low_select = IDLE; #10;
            acc_in_select = DATA_ALU; acc_high_select = LOAD; #10;
            acc_high_select = SHIFT_LEFT; acc_low_select = SHIFT_LEFT; #40;               // 좌shift
            
            $stop;
      end
      
      
endmodule

 

shift 연산 시뮬레이션 결과

80ns~120ns  ->  우shift
160ns~200ns  ->  좌shift

 

 

testbench_acc 소스코드(좌 shift, 상위 4비트 초기화 확인)
//////////////////////////////////////////////// 2024.10.14
module tb_acc();

      parameter IDLE = 2'b00;
      parameter SHIFT_RIGHT = 2'b01;
      parameter SHIFT_LEFT = 2'b10;
      parameter LOAD = 2'b11;
      parameter DATA_ALU = 0;
      parameter DATA_BUS = 1;
      
      // 원래 코드에서의 input -> reg  ,  output -> wire로 설정
      reg clk, reset_p, acc_high_reset_p;
      reg fill_value, rd_en, acc_in_select;
      reg [1:0] acc_high_select, acc_low_select;
      reg [3:0] bus_data, alu_data;
      wire [3:0] high_data2bus, acc_high_data2alu;
      wire [3:0] low_data2bus, acc_low_data;
      
      ACC DUT(
            clk, reset_p, acc_high_reset_p,
            fill_value, rd_en, acc_in_select,
            acc_high_select, acc_low_select,
            bus_data, alu_data,
            high_data2bus, acc_high_data2alu,
            low_data2bus, acc_low_data);
      
      // 초기화 설정
      initial begin
            clk = 0; reset_p = 1; acc_high_reset_p = 0;
            fill_value = 0;
            rd_en = 1;
            acc_in_select = 0;
            acc_high_select = 0;
            acc_low_select = 0;
            bus_data = 4'b0010;
            alu_data = 4'b0101;
      end
      
      // 클록 설정
      always #5 clk = ~clk;
      
      // 결과값 확인
      initial begin
            #10;
            reset_p = 0; #10;
            acc_high_select = LOAD; #10;             //alu 데이터 로드
            acc_high_select = IDLE; #10;
            acc_in_select = DATA_BUS; acc_high_select = LOAD; #10;            // bus 데이터 로드
            acc_high_select = IDLE; acc_low_select = LOAD; #10;        // 상위 비트 데이터 로드
            acc_low_select = IDLE; #10;
            acc_in_select = DATA_ALU; acc_high_select = LOAD; #10;
            acc_high_select = SHIFT_RIGHT; acc_low_select = SHIFT_RIGHT; #40;             // 우shift
            
            acc_in_select = DATA_BUS; acc_high_select = LOAD; #10;            //bus 데이터를 상위 비트 데이터에 load
            acc_high_select = IDLE; acc_low_select = LOAD; #10;        // 상위 비트 데이터 로드
            acc_low_select = IDLE; #10;
            acc_in_select = DATA_ALU; acc_high_select = LOAD; #10;
            acc_high_select = SHIFT_LEFT; acc_low_select = SHIFT_LEFT; #20;               // 좌shift
            acc_high_select = IDLE; acc_low_select = IDLE;                                                 // 상위 비트 리셋을 위해 좌shift 초기화
            
            // 상위 비트 리셋
            acc_high_reset_p = 1; #10;          
            acc_high_reset_p = 0; #10;
            
            $stop;
      end
      
      
endmodule

 

아래의 상자 부분 처럼 수정하여 좌shift 되는 시간을 변경하였고 상위 비트를 초기화 하는 것을 추가하였다.

 

 

shift 연산 및 비트 초기화 시뮬레이션 결과

160ns~180ns  ->  좌shift
180ns~200ns  ->  상위 4비트 초기화


cpu의 구조와 동작원리1(ALU, ACC) 끝!