본문 바로가기
Digital Logic/Verilog

[Verilog] Fixed point Scaler 연산 (Quantization - 비트 수가 다른 파라미터와 연산)

by 고뭉나무 2023. 9. 6.

Pytorch에서 quantization을 구현 코드를 verilog로 변환하기 위해서는 fixed point scaler 연산이 필요하다.

연산에 있어서 필요한 여러 개념들이 있는데 아래 블로그 글을 참고하면 좋다.

2023.09.05 - [Digital Logic/Verilog] - [Verilog] Fixed point Scaler 연산 (Quantization - binary 연산)

2023.09.06 - [Digital Logic/Verilog] - [Verilog] Fixed point Scaler 연산 (Quantization - sign 곱셈/덧셈 연산)

 

[Verilog] Fixed point Scaler 연산 (Quantization - binary 연산)

Fixed point 란? verilog로 프로그래밍 하기 위해서는 값의 형태가 fixed point로 구성되어야 한다. 위의 그림처럼 sign / Integer / Fraction 으로 되어 있다. 특히 quantization의 경우, Integer와 scaler로 나눠서 연산

rubber-tree.tistory.com

 

비트 수가 다른 bias와 덧셈 연산

이번에는 convolution 연산 후에 bias를 더해주는 작업을 한다.

여기서 주의할 점은 Q_conv와 bias의 bit 수가 다르다.

정확하게는 둘의 bit수를 동일하게 맞추어 메모리에 저장할 수 있지만 값이 더 적은 bias는 그럴 필요가 없다.

메모리 용량(혹은 버퍼)을 많이 차지하는 것은 결코 좋은 일이 아니기에 더 적은 bit를 할당한다.

그렇게 메모리에서 불러온 둘을 사칙 연산해야 하는데 이때 발생하는 이슈가 있다.

 

pytorch 코드는 아래와 같다.

Q_w, S_w = self.quant_weight(self.weight)  # Quantized Integer, Float Scale
Q_conv = nn.functional.conv2d(Q_x, Q_w, None, self.stride, self.padding, self.dilation, self.groups)

// scaling
Q_conv = Q_conv * S_y

// bias
Q_conv = Q_conv + bias

// rounding
Q_conv = torch.round(Q_conv)
// clamping
Q_conv = torch.clamp(Q_conv, -self.qa.a_qmax, self.qa.a_qmax)

 

비트 수가 다른 두 파라미터를 verilog로 구현하기 위해서는 아래 조건을 만족해야 한다.

  • 비트 수를 맞추기 위해 sign extension하고
  • fraction 위치를 맞추기 위해 shift 함

 

module ConvScalerUnit #(
   parameter INP_WL = 40,
   parameter RF_WL = 16,	// Register File (bias)
   parameter SCALE_WL = 14,
   parameter SCALE_FL = 13
)(
   input wire clk,
   input wire reset,

   input wire [INP_WL-1:0] din,
   output wire [INP_WL-1:0] dout
);
    wire [INP_WL-1:0] psum_bias;
    // rf_rd_data: bias value (16bit)
    // scaling psum
    wire [SCALE_WL-1:0] s_y;
    assign s_y = 14'b00_0000_0011_1001; // 0.00695801  // sign bit

    wire [INP_WL+SCALE_WL-1:0] scaled_psum = $signed(din) * $signed(s_y);  //it's better that both are casted to $signed.

    // convert from 16bit to 54bit
    wire [INP_WL+SCALE_WL-1:0] bias_54bit; // (40 + 14)
    // sign extension and left shift
    assign bias_54bit = {{(INP_WL+SCALE_WL-RF_WL+1){rf_rd_data[RF_WL-1]}}, rf_rd_data[RF_WL-2:0]} <<< 7;  //sign extension n shift

    // add
    wire [INP_WL+SCALE_WL-1:0] psum_bias_54 = $signed(scaled_psum) + $signed(bias_54bit);
    
    // round
    wire [INP_WL-1:0] round_psum_bias;
    SignedRoundSaturate #(
      .IN_WLEN(INP_WL+SCALE_WL),
      .OUT_WLEN(INP_WL+SCALE_WL-SCALE_FL),
      .IN_FLEN(SCALE_FL)
    ) ROUND_PSUM_BIAS1 (
      .din(psum_bias_54),
      .dout(round_psum_bias)
    );

  	// 41bit -> 16bit clipping
  	wire [OUT_WL-1:0] clip_psum = $signed({round_psum_bias[INP_WL-1], round_psum_bias[OUT_WL-2:0]});
  
  	// clamp
  	wire [OUT_WL-1:0] clamp_max = 16'd65535;
  	assign psum_scaled = (clip_psum < 0) ? 0 : (clip_psum > clamp_max) ? clamp_max : clip_psum; // pytorch code: -2^15 ~ 2^15 

endgenerate

 

그림으로 묘사하면 다음과 같다.

반응형

댓글