// part of NeoGS project (c) 2007-2008 NedoPC
 
//
 
// sound_mulacc is a serial multiplier-accumulator for volume and sound data value.
 
// Input data is: volume (6bit unsigned) and sound data (8bit signed with sign bit inverted,
 
//  thus compatible with unsigned data centered at $7f-$80), and clr_sum bit.
 
// Input data is read and multiply-add starts after 1-clock positive pulse on load pin,
 
//  when ready becomes 1, operation is finished, output is defined and another load pulse could be accepted.
 
// If clr_sum is read to be 1, sum is also cleared (and the output will be just result of multiply), otherwise
 
//  output will be the sum of previous result with current multiplication result.
 
//
 
// clock number          XX |  00 |  01 |  02 |  || |  14 |  15 |  16 |
 
// clock            __/``\__/``\__/``\__/``\__/``||_/``\__/``\__/``\__/``\__/``\__/``\__/``\__/``
 
// load       _________/`````\___________________||______________________________________________
 
// inputs are read here --> *                    ||
 
// ready      ```````````````\___________________||______________/```````````````````````````````
 
// data out               old data |XXXXXXXXXXXXX||XXXXXXXXXXXXXX| new data ready
 
 
 
module sound_mulacc(
 
 
 
        clock,   // input clock (24 MHz)
 
 
 
        vol_in,  // input volume (6 bit unsigned)
 
        dat_in,  // input sound data (8 bit signed with sign bit inverted)
 
 
 
        mode_inv7b, // whether to invert 7th bit of dat_in
 
 
 
        load,    // load pulse input
 
        clr_sum, // clear sum input
 
 
 
        ready,   // ready output
 
        sum_out  // 16-bit sum output
 
);
 
 
 
        input clock;
 
 
 
        input [5:0] vol_in;
 
        input [7:0] dat_in;
 
 
 
        input mode_inv7b;
 
 
 
        input load;
 
        input clr_sum;
 
 
 
        output reg ready;
 
 
 
        output reg [15:0] sum_out;
 
 
 
 
 
 
 
        wire [5:0] add_data;
 
        wire [6:0] sum_unreg;
 
        reg [6:0] sum_reg;
 
        reg [7:0] shifter;
 
        reg [5:0] adder;
 
        wire   mul_out;
 
 
 
 
 
 
 
        reg [3:0] counter;
 
 
 
 
 
        reg clr_sum_reg;
 
        wire [1:0] temp_sum;
 
        wire carry_in;
 
        wire old_data_in;
 
        reg old_carry;
 
 
 
 
 
 
 
 
 
        // control section
 
        //
 
        always @(posedge clock)
 
        begin
 
 
 
                if( load )
 
                        ready <= 1'b0;
 
 
 
                if( counter[3:0] == 4'd15 )
 
                        ready <= 1'b1;
 
 
 
                if( load )
 
                        counter <= 4'd0;
 
                else
 
                        counter <= counter + 4'd1;
 
 
 
        end
 
 
 
 
 
 
 
        // serial multiplier section
 
        //
 
        assign add_data = ( shifter[0] ) ? adder : 6'd0; // data to be added controlled by LSB of shifter
 
 
 
        assign sum_unreg[6:0] = sum_reg[6:1] + add_data[5:0]; // sum of two values
 
 
 
        assign mul_out = sum_unreg[0];
 
 
 
        always @(posedge clock)
 
        begin
 
 
 
                if( !load ) // normal addition
 
                begin
 
                        sum_reg[6:0] <= sum_unreg[6:0];
 
                        shifter[6:0] <= shifter[7:1];
 
                end
 
 
 
                else // load==1
 
 
 
                begin
 
                        sum_reg[6:0] <= 7'd0;
 
 
 
                        shifter[7]   <= ~(mode_inv7b^dat_in[7]);   // convert to signed data (we need signed result), invert 7th bit if needed
 
                        shifter[6:0] <=  dat_in[6:0];
 
 
 
                        adder <= vol_in;
 
 
 
                end
 
        end
 
 
 
 
 
 
 
 
 
        // serial adder section
 
        //
 
        always @(posedge clock)
 
        begin
 
                if( load )
 
                        clr_sum_reg <= clr_sum;
 
        end
 
 
 
        assign carry_in = (counter==4'd0) ? 1'b0 : old_carry;
 
 
 
        assign old_data_in = (clr_sum_reg) ? 1'b0 : sum_out[0];
 
 
 
        assign temp_sum[1:0] = carry_in + mul_out + old_data_in;
 
 
 
        always @(posedge clock)
 
        begin
 
                if( !ready )
 
                begin
 
                        sum_out[15:0] <= { temp_sum[0], sum_out[15:1] };
 
                        old_carry <= temp_sum[1];
 
                end
 
        end
 
 
 
 
 
 
 
endmodule