Jump to content
    

преобразование int32 в int16 на verilog

Всем доброго дня.

Надо по сути 32-разрядное знаковое число сократить до 16-разрядного знакового.

по сути это будет 

outdata_reg <= indata/65536; 

но поскольку на младших циклонах знак деления штука не экономная то написал так:
 

module conv_sign_32to16 (
	input clk, 
	input signed   [31:0] indata,
	output signed  [15:0] outdata
	);
	
	reg [15:0] outdata_reg;
	
	always @ (posedge clk)
		begin
		outdata_reg <= ((indata + (32'd1<<31)) >> 16) - (16'd1<<15); 
		end //always

	assign outdata = outdata_reg;
		
endmodule

может есть способ элегантнее? Заранее спасибо.

Share this post


Link to post
Share on other sites

3 minutes ago, Halfback said:

может есть способ элегантнее? Заранее спасибо.

нет. у вас есть floor/ceil/round вот их в рукопашку и выбирайте исходя из задач

PS. деление на 2^N современные синтезаторы распознают, некоторые распознают и честное деление, а некоторые даже округление правильное делают

Share this post


Link to post
Share on other sites

20 minutes ago, Halfback said:

по сути это будет 

outdata_reg <= indata/65536; 

Раз так, то почему нельзя просто записать ?

outdata_reg[15:0] <= indata[31:16];

 

Share this post


Link to post
Share on other sites

2 hours ago, dimka76 said:

Раз так, то почему нельзя просто записать ?

outdata_reg[15:0] <= indata[31:16];

 

Это floor, округление к нулю для положительных чисел и к минус бесконечности для отрицательных. Может привести к паразитному смещению, при накоплении.

Share this post


Link to post
Share on other sites

45 минут назад, dimka76 сказал:

Раз так, то почему нельзя просто записать ?

outdata_reg[15:0] <= indata[31:16];

 

на все отрицательные числа от -1 до -65355 результат будет "-1" а д.б. "0".

кому это не критично - скорее всего так и делают.

Share this post


Link to post
Share on other sites

1 hour ago, Halfback said:

на все отрицательные числа от -1 до -65355 результат будет "-1" а д.б. "0".

кому это не критично - скорее всего так и делают.

когда ковырялся в теме округления, то ИМХО главное принять решение что делать с округлением точки 0.5. Ваш код например округляет 1.5 к 1, а не 2.0. Поэтому и написал что надо определиться что именно нужно по ТЗ. Если просто нужно округление положительных и отрицательных чисел к нулю, то можно просто прибавить к результату знаковый разряд. Будет чуть меньше сложений. Но 0.5 округляться будет не корректно.

Share this post


Link to post
Share on other sites

экстраполировал предоставленный вариант в конвертор 16 в 8 бит. Так меньше диапазон перебираемых чисел. Ну и добавил к сравнению floor/floor с округлением отрицательных чисел к нулю/round(x) ~= floor(x+0.5)

module tb ();

  initial begin
    int tmp;

    int err;

    logic signed [7 : 0] rslt1, rslt2, rslt3, rslt4;

    for (int i = -(2**15); i <= 2**15-1; i += 100) begin

      tmp   = $signed(i[15 : 0]);

      rslt1 = conv_16to8(tmp);
      rslt2 = floor(tmp);
      rslt3 = round(tmp);
      rslt4 = floor2(tmp);

      if ((rslt1 != rslt2) || (rslt1 != rslt3) || (rslt1 != rslt4)) begin
        $display("error %0d = %0f", tmp, tmp*1.0/256);
        $display("conv   %0d", rslt1);
        $display("floor  %0d", rslt2);
        $display("round  %0d", rslt3);
        $display("floor2 %0d", rslt4);

        err++;
      end
    end

    $display("errs = %0d", err);
    $stop;
  end


  function logic signed [7 : 0] conv_16to8 (input logic signed [15 : 0] indata);
    logic signed [7 : 0] outdata;
    //
    outdata = ((indata + (16'd1<<15)) >> 8) - (8'd1<<7);
    //
    return outdata;
  endfunction

  function logic signed [7 : 0] floor (input logic signed [15 : 0] indata);
    logic signed [7 : 0] outdata;
    //
    outdata = indata >>> 8;
    //
    return outdata;
  endfunction

  function logic signed [7 : 0] floor2 (input logic signed [15 : 0] indata);
    logic signed [7 : 0] outdata;
    //
    outdata = indata >>> 8;
    outdata += (indata < 0) ? 1 : 0;
    //
    return outdata;
  endfunction

  function logic signed [7 : 0] round (input logic signed [15 : 0] indata);
    logic signed [8 : 0] outdata;
    logic signed [16 : 0] tmp;
    //
    tmp = indata + 128;

    outdata = (tmp) >>> 8;
    if (outdata > 127) begin
      outdata = 127;
    end
    //
    return outdata[7 : 0];
  endfunction

endmodule

можно сравнить "тяжесть" функции округления и качество результата. Вот несколько интересных точек

# error -1368 = -5.343750
# conv   -6
# floor  -6
# round  -5
# floor2 -5
# error -1268 = -4.953125
# conv   -5
# floor  -5
# round  -5
# floor2 -4
# error 28032 = 109.500000
# conv   109
# floor  109
# round  110
# floor2 109
# error 28132 = 109.890625
# conv   109
# floor  109
# round  110
# floor2 109  

 

Share this post


Link to post
Share on other sites

des00

Спасибо, интересно.

Посмотрев Ваш код я не понял почему Вы так делаете 

outdata = indata >>> 8;

а не так 

outdata = indata >> 8;

 

 

Share this post


Link to post
Share on other sites

26 minutes ago, Halfback said:

des00

Спасибо, интересно.

Посмотрев Ваш код я не понял почему Вы так делаете 

outdata = indata >>> 8;

а не так 

outdata = indata >> 8;

 

 

Очевидно же - это арифметический сдвиг, для сохранения знакового разряда (заполнять 1-цами или нулями, в зависимости от старшего бита)

Share this post


Link to post
Share on other sites

module conv_sign_32to16 (
	input clk, 
	input signed   [31:0] indata,
	output signed  [15:0] outdata
	);
	
	reg [15:0] outdata_reg;
	
	always @ (posedge clk)
		begin
		outdata_reg <= ((indata>>> 16) + (indata[15])); 
		end //always

	assign outdata = outdata_reg;
		
endmodule

А я слышал про такой вариант - сдвиг и суммирование старшего отбрасываемого бита.

Share this post


Link to post
Share on other sites

Приветствую!

31 minutes ago, Lmx2315 said:

А я слышал про такой вариант - сдвиг и суммирование старшего отбрасываемого бита.

Только при этом надо проверять сдвинутое число на  максимальное положительное  0x7FFF,  а  иначе  можно получить проблему с переполнением.

 

Удачи! Rob. 

Share this post


Link to post
Share on other sites

21 минуту назад, RobFPGA сказал:

Приветствую!

Только при этом надо проверять сдвинутое число на  максимальное положительное  0x7FFF,  а  иначе  можно получить проблему с переполнением.

 

Удачи! Rob. 

если применяем для DSP обработки то можно не проверять, обычно обработка идёт с запасом до верхнего края, а округления происходят в конце обработки.

Share this post


Link to post
Share on other sites

Приветствую!

26 minutes ago, Lmx2315 said:

если применяем для DSP обработки то можно не проверять

???  :shok:  

То есть на  входе + 0x7FFF_8000  вроде и с запасом,  а на выходе - 0x8000   :wacko2: и можно не проверять ....   

 

Удачи! Rob.

Share this post


Link to post
Share on other sites

22 минуты назад, RobFPGA сказал:

Приветствую!???

  :shok:  То есть на  входе + 0x7FFF_8000  вроде и с запасом,  а на выходе - 0x8000   :wacko2: и можно не проверять ....   

между 0x7fff где будет переполнение и 0x7ffe где нет переполнения - 0.000265 дб разницы , вот при таком запасе можно и не проверять.

Share this post


Link to post
Share on other sites

21 hours ago, Lmx2315 said:

	

точно, получится тот же floor(x+0.5)

20 hours ago, RobFPGA said:

Только при этом надо проверять сдвинутое число на  максимальное положительное  0x7FFF,  а  иначе  можно получить проблему с переполнением.

добавить бит к сумме и проверить 01 комбинацию старших двух бит. может быть будет чуть быстрее чем весь вектор ANDить

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...