//# Noise Shaper // Truncates a stream of signed integer values to a narrower bit width, and adds the // lost least-significant bits back to the next input value before its truncation. // This first order noise shaping reduces the error from truncation, assuming // the stream is a time-series of some sort (i.e.: not independently random // numbers). `default_nettype none module Noise_Shaper #( parameter INPUT_WIDTH = 25, parameter OUTPUT_WIDTH = 16 ) ( input wire clock, input wire clear, input wire [INPUT_WIDTH-1:0] data_in, output wire [OUTPUT_WIDTH-1:0] data_out ); localparam INPUT_ZERO = {INPUT_WIDTH{1'b0}}; localparam LOST_WIDTH = INPUT_WIDTH - OUTPUT_WIDTH; localparam LOST_ZERO = {LOST_WIDTH{1'b0}}; // First, truncate `data_in` and replace the lost bits with zeros. reg [INPUT_WIDTH-1:0] data_in_truncated = INPUT_ZERO; always @(*) begin data_in_truncated = {data_in [INPUT_WIDTH-1 -: OUTPUT_WIDTH], LOST_ZERO}; end // Then subtract the truncated value from the original, giving us the // `truncation_error`. wire [INPUT_WIDTH-1:0] truncation_error; Adder_Subtractor_Binary #( .WORD_WIDTH (INPUT_WIDTH) ) calculate_truncation_error ( .add_sub (1'b1), // 0/1 -> A+B/A-B .carry_in (1'b1), // Subtraction, so reversed meaning .A (data_in), .B (data_in_truncated), .sum (truncation_error), // verilator lint_off PINCONNECTEMPTY .carry_out (), .carries (), .overflow () // verilator lint_on PINCONNECTEMPTY ); // Then store the `truncation_error` for 1 cycle so we can add it to the next // `data_in`. This also breaks a combinational loop. wire [INPUT_WIDTH-1:0] truncation_error_previous; Register #( .WORD_WIDTH (INPUT_WIDTH), .RESET_VALUE (INPUT_ZERO) ) error_storage ( .clock (clock), .clock_enable (1'b1), .clear (clear), .data_in (truncation_error), .data_out (truncation_error_previous) ); // Add the previous truncation error to the current input data, before it is // truncated. wire [INPUT_WIDTH-1:0] data_in_adjusted; Adder_Subtractor_Binary #( .WORD_WIDTH (INPUT_WIDTH) ) add_lost_data ( .add_sub (1'b0), // 0/1 -> A+B/A-B .carry_in (1'b0), .A (data_in), .B (truncation_error_previous), .sum (data_in_adjusted), // verilator lint_off PINCONNECTEMPTY .carry_out (), .carries (), .overflow () // verilator lint_on PINCONNECTEMPTY ); // Finally, truncate the adjusted input data. Width_Adjuster #( .WORD_WIDTH_IN (INPUT_WIDTH), .SIGNED (1), .WORD_WIDTH_OUT (OUTPUT_WIDTH) ) truncate_output ( // It's possible some input bits are truncated away // verilator lint_off UNUSED .original_input (data_in_adjusted), // verilator lint_on UNUSED .adjusted_output (data_out) ); endmodule