본문 바로가기
Digital Logic/Verilog

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

by 고뭉나무 2023. 9. 5.

 

 

Fixed point 란?

verilog로 프로그래밍 하기 위해서는 값의 형태가 fixed point로 구성되어야 한다.

위의 그림처럼 sign / Integer / Fraction 으로 되어 있다. 

특히 quantization의 경우, Integer와 scaler로 나눠서 연산하며 scaler의 integer와 fraction이 각각 몇 bit인지를 알고 있어야 된다.

 

Fixed point Scaler 연산

아래와 같이 quantization하는 pytorch 코드가 있다.

// rescaling
rescale = self.a_max / self.a_max_origin
inp = inp * rescale

// rounding
inp = torch.round(inp)

inp = inp * (1. / self.fqa.alpha)

// rounding
inp_Q = torch.round(inp)

// clamping
inp_Q = torch.clamp(inp_Q, 0, self.fqa.a_qmax)

 

이를 verilog로 구현하기 위해서는 아래와 같은 조건이 필요하다.

  • Q_conv는 input에 따라 변경되는 integer 값일 것
  • S_y는 고정된 값이므로 측정을 통해 알아두고 verilog 코드에 fixed 해둠.
  • rounding에서는 scaler의 fraction bit만큼 잘라내면 됨.
  • rounding을 통해 scaler의 sign과 integer bit이 남고 이는 output bit를 넘어가게 함. 이때 임의로 맵핑하면 MSB bit을 가지고 함. rounding 후 값이므로 MSB bit만 중요함.
  • clamping에서는 Q_conv에 할당된 bit의 min, max 기준으로 쳐냄.

 

 

위 조건을 따라서 verilog 코드를 짜면 다음과 같다.

module Scaler #(
	parameter SCALE_WL = 16,	// whole length  
	parameter SCALE_FL = 14,	// fraction length
	parameter INP_WL = 16,
	parameter OUT_WL = 16,

 	parameter RESCALE_WL = 18,		// integer bit: 6
 	parameter RESCALE_FL = 12,      // fraction bit: 12
    
    parameter ALPHA_WL = 21,
    parameter ALPHA_FL = 20      	
)(
	input wire clk,
	input wire reset,

	input wire [SCALE_WL-1:0] scale,

	input wire [INP_WL-1:0] data_in,
	input wire valid_in,

	output wire [OUT_WL-1:0] data_out,
	output wire valid_out
);

  wire [RESCALE_WL-1:0] s_y;
  // 측정한 특정 layer의 s_y 값 (sign, integer, fraction 순)
  assign s_y = {{1'b0}, {5'b10000}, {12'b000000001111}};
  
  wire [INP_WL+RESCALE_WL-1:0] mult0 = $signed(data_in) * $signed(s_y);    // data_in = Q_conv
  wire [INP_WL+RESCALE_WL-RESCALE_FL-1:0] round_mult0;  
  
  // rounding
  SignedRoundSaturate #(
    .IN_WLEN(INP_WL+RESCALE_WL),
    .OUT_WLEN(INP_WL+RESCALE_WL-RESCALE_FL),
    .IN_FLEN(RESCALE_FL)
  ) ROUND_MULT0 (
    .din(mult0),
    .dout(round_mult0)
  );
  
 
  wire [ALPHA_WL-1:0] fqa_alpha;     // <= 1 / self.fqa.alpha
  assign fqa_alpha = 21'b0_1111_1111_0110_1001_1011; // 0.99770606  // sign bit

  // calculate
  wire [INP_WL+RESCALE_WL-RESCALE_FL+ALPHA_WL-1:0] mult1 = $signed(round_mult0) * $signed(fqa_alpha);  // signed casting
  wire [INP_WL+RESCALE_WL-RESCALE_FL-1:0] round_mult1;


  // round
  SignedRoundSaturate #(
    .IN_WLEN(INP_WL+RESCALE_WL-RESCALE_FL+ALPHA_WL),
    .OUT_WLEN(INP_WL+RESCALE_WL-RESCALE_FL+ALPHA_WL-ALPHA_FL),
    .IN_FLEN(ALPHA_FL)
  ) ROUND_MULT1 (
    .din(mult1),
    .dout(round_mult1)
  );


  // clamp
  wire [15:0] clamp_max = 16'd65535;
  wire [OUT_WL-1:0] clamp = (round_mult1 < 0) ? 0 : (round_mult1 > clamp_max) ? clamp_max : round_mult1;
반응형

댓글