본문 바로가기
Digital Logic/Verilog

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

by 고뭉나무 2023. 9. 6.

 

Fixed point Scaler 연산의 기초는 아래 블로그 글을 참고하면 좋다.

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

 

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

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

rubber-tree.tistory.com

 

Scaling 연산

Residual add

아래 residual add 연산의 quantization한 pytorch 코드가 있다.

// scaling
Q_conv = Q_1 * S_y1
Q_conv2 = Q_2 * S_y2
        
// add
Q_conv = Q_conv + Q_conv2

// rounding
Q_conv = torch.round(Q_conv)

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

 

이를 verilog로 짜려면 아래 사항을 주의해야 한다.

  • signed 곱셈을 하려면 곱셈 인자 모두 signed 처리 해야 함. (아니면 다소 복잡함)
  • 곱셈 인자 중 하나라도 '음수'이면, 나머지 하나도 $signed(_) 되어야 계산 상 오류가 안 생김.
  • scale을 signed로 타입 캐스팅 하려면 sign bit (1 bit) 꼭 넣어주어야 함.
  • 특정 wire에 sign을 가지게 하려면, 애초에 wire 선언할 때 wire signed로 하거나 연산 할 때 파라미터 앞에 $signed(_) 붙여줌.

 

module ResidLoadUnit_Exec #(
	parameter INP_WL = 16,
	parameter SCALE0_WL = 14, // skip (res)
	parameter SCALE0_FL = 13, // 
	parameter SCALE1_WL = 14, // for sign bit
	parameter SCALE1_FL = 13
)(
	input wire clk,
	input wire reset,

	input wire [INP_WL-1:0] din_inp,
	input wire [INP_WL-1:0] din_res,
	output wire [INP_WL-1:0] dout
);
  
  // middle_blks.0.resq1 
  wire [SCALE0_WL-1:0] scale0 = 14'b01_1111_1111_1111; // 0.9998779
  wire [SCALE1_WL-1:0] scale1 = 14'b00_1110_1111_0111; // 0.46765137

  wire [INP_WL+SCALE0_WL-1:0] Q_conv0 = $signed(din_inp) * $signed(scale0);
  wire [INP_WL+SCALE1_WL-1:0] Q_conv1 = $signed(din_res) * $signed(scale1);

  // #bits of SCALE0 = SCALE1
  wire [INP_WL+SCALE0_WL-1:0] AddQ = (Q_conv0) + (Q_conv1); 
  wire [INP_WL+SCALE0_WL-SCALE0_FL-1:0] round_AddQ;

  // round
  SignedRoundSaturate #(
    .IN_WLEN(INP_WL+SCALE0_WL),
    .OUT_WLEN(INP_WL+SCALE0_WL-SCALE0_FL),
    .IN_FLEN(SCALE0_FL)
  ) ROUND_MULT0 (
    .din(AddQ),
    .dout(round_AddQ)
  );

  wire [INP_WL-1:0] sign_round_AddQ = $signed(round_AddQ);
  //clamp (depends on self.qa.pos)
  wire [INP_WL-1:0] clamp_max = 16'd65535;

  wire [INP_WL-1:0] result = (sign_round_AddQ < 0) ? 0 : (sign_round_AddQ > clamp_max) ? clamp_max : sign_round_AddQ;  

endmodule

 

 

 

 

 

반응형

댓글