Top secrets sources NedoPC ngs

Rev

Rev 91 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed | ?url?

// part of NeoGS project (c) 2007-2008 NedoPC
//
// sound_main is the main sound module: it incorporates data storage (512b memory), from which
//  it reads data, prepares it through sound_mulacc and sends to sound_dac. It incorporates in itself
//  sound_dac, so it has outs to the DAC.
// clock is ordinary 24 MHz clock, mode_8chans is asynchronous input signal controlling mode of operation,
//  either 4 or 8 channels.
//
// channels in 4 channel mode (mode_8chans=0,mode_pan4ch=0)
// 1,2 -> left
// 3,4 -> right
// channels in 8 channel mode (mode_8chans=1,mode_pan4ch=0)
// 1,2,5,6 -> left
// 3,4,7,8 -> right
// channels in panning 4 channel mode (mode_8chans=0,mode_pan4ch=1)
// 1,2,3,4 (vols 1,2,5,6) -> left
// 1,2,3,4 (vols 3,4,7,8) -> right
// channels in mem are at addressed 0-7 (corresponding to channels 1-8, respectively).
// mem contains volumes (lower 6 bits, 0-zero volume, 63-max volume) and sound data (signed value with sign inverted:
// -data in mem---+----value--
//          $FF   |     +$7F
//          $81   |     +$01
//          $80   |     +$00
//          $7F   |     -$01 (or $FF)
//          $01   |     -$7F (or $81)
//          $00   |     -$80 (or $80)
// alternatively, it could be treated as unsigned positive samples with middle point of $7F-$80.
//
// inv7b causes 7th bit inversion: samples become signed
//
// clock      ``\__/``\__/``\__/``\__/``\__/``\__/``\__/``\__
// mem_read   ______/`````\__________________________________
// mem_wraddr       |  no |     |addr |
// mem_di           |write|     |data |
// mem_***_we ______|here!|_____/`````\______________________
//                                   ^-- data written here!

module sound_main(

        clock,         // system clock (24 MHz)

        mode_8chans,   // =1 - 8 channels, =0 - 4 channels
        mode_pan4ch,   // =1 - 4 channels with panning
        mode_inv7b,    // =1 - invert 7th bit of every sample byte


        in_wrtoggle,   // from ports.v module (async to clock)
        in_datnvol,    //
        in_wraddr,     //
        in_data,       //


        dac_clock,     // output to DAC
        dac_leftright, // output to DAC
        dac_data       // output to DAC
);

    // input-output description

        input clock;

        input mode_8chans;
        input mode_pan4ch;
        input mode_inv7b;

        input in_wrtoggle;
        input in_datnvol;
        input [2:0] in_wraddr;
        input [7:0] in_data;

        output dac_clock;

        output dac_leftright;

        output dac_data;


        // internal regs/wires

        reg mem_read; // write to mem is forbidden while mem_read=1

        reg datnvol;

        reg [5:0] vol; // temporary storage for volume

        reg mem_we; // write strobe

        reg wrtgl1, wrtgl2, wrtgl3; // sync in and edge detect of in_wrtoggle

        reg do_write; // indicates that write should be performed

        reg [2:0] bf_wraddr;
        reg [7:0] bf_data;
        reg       bf_datnvol;



        wire dac_load; // signal from sound_dac module (when it loads new data)

        wire [15:0] dac_pardata; // parallel data from sound_mulacc to sound_dac

        reg mulacc_load;   // load to sound_mulacc
        reg mulacc_clrsum; // clr_sum to sound_mulacc
        wire mulacc_ready; // ready from sound_mulacc

        wire [7:0] mem_do; // data output of DAT or VOL

        reg [8:0] mem_rdaddr;  // read address for both memory blocks

        reg int_mode_8chans;  // internal and sync-in mode_8chans signals
        reg sync_mode_8chans; //

        reg int_mode_pan4ch,sync_mode_pan4ch; // same for pan4ch signal
        reg int_mode_inv7b, sync_mode_inv7b;  // ...

        reg [1:0] chanptr; // pointer to channels (4 channels total: 0,1,4,5 or 2,3,6,7 depending on lrptr state)
        reg lrptr;         // left-right pointer (selects either left (0) or right (1) channels)




        reg [2:0] cur_st,nxt_st;









        // instantiating modules

        sound_dac my_dac( .clock(clock),
                          .dac_clock(dac_clock),
                          .dac_leftright(dac_leftright),
                          .dac_data(dac_data),
                          .load(dac_load),
                          .datain(dac_pardata) );

        sound_mulacc my_mulacc( .clock(clock),
                                .vol_in(vol),
                                .dat_in(mem_do),
                                .mode_inv7b(int_mode_inv7b),
                                .load(mulacc_load),
                                .clr_sum(mulacc_clrsum),
                                .ready(mulacc_ready),
                                .sum_out(dac_pardata) );

        // DAT-VOL memory block
        mem512b my_mem( .clk(clock),
                        .re(1'b1),
                        .rdaddr(mem_rdaddr),
                        .dataout(mem_do),
                        .wraddr({5'b0,bf_datnvol,bf_wraddr}),
                        .datain(bf_data),
                        .we(mem_we) );





        // syncing in asynchronous control signals
        always @(posedge clock)
        begin
                { int_mode_8chans,sync_mode_8chans } <= { sync_mode_8chans, mode_8chans };
                { int_mode_pan4ch,sync_mode_pan4ch } <= { sync_mode_pan4ch, mode_pan4ch };
                { int_mode_inv7b ,sync_mode_inv7b  } <= { sync_mode_inv7b,  mode_inv7b  };
        end


        // load lrptr (left-right pointer) on dac_load pulse
        always @(posedge clock)
        begin
                if( dac_load )
                        lrptr <= ~dac_leftright;
        end

        // make memory read address from chanptr and lrptr
        always @*
        begin
/*              mem_rdaddr[8:4] <= 5'd0;
                mem_rdaddr[3]   <= datnvol;
                mem_rdaddr[2]   <= int_mode_8chans ? chanptr[1] : 1'b0;
                mem_rdaddr[1]   <= lrptr;
                mem_rdaddr[0]   <= chanptr[0];*/


                mem_rdaddr[8:4] <= 5'd0;

                if( int_mode_8chans )
                begin
                        mem_rdaddr[3]   <= datnvol;
                        mem_rdaddr[2]   <= chanptr[1];
                        mem_rdaddr[1]   <= lrptr;
                        mem_rdaddr[0]   <= chanptr[0];
                end
                else if( int_mode_pan4ch )
                begin
                        mem_rdaddr[3] <= datnvol;

                        if( datnvol ) // sample data
                        begin
                                mem_rdaddr[2] <= 1'b0;
                                mem_rdaddr[1] <= chanptr[1];
                                mem_rdaddr[0] <= chanptr[0];
                        end
                        else // !datnvol: volumes
                        begin
                                mem_rdaddr[2]   <= chanptr[1]; // same as in 8 channel
                                mem_rdaddr[1]   <= lrptr;
                                mem_rdaddr[0]   <= chanptr[0];
                        end
                end
                else //normal 4 channel mode
                begin
                        mem_rdaddr[3]   <= datnvol;
                        mem_rdaddr[2]   <= 1'b0;
                        mem_rdaddr[1]   <= lrptr;
                        mem_rdaddr[0]   <= chanptr[0];
                end
        end

        // handle mulacc_clrsum signal
        always @(posedge clock)
        begin
                if( dac_load )
                        mulacc_clrsum <= 1'b1;     // set on dac_load pulse
                else if( mulacc_load )
                        mulacc_clrsum <= 1'b0;     // clear on mulacc_load pulse, so only first mulacc cycle will read clrsum high
        end





        localparam START      = 0;
        localparam LOAD_VOL   = 1;
        localparam LOAD_VOL2  = 2;
        localparam LOAD_DAT   = 3;
        localparam LOAD_DAT2  = 4;
        localparam START_MUL  = 5;
        localparam WAIT_STOP  = 6;
        localparam LOOP       = 7;



        // for simulation purposes
        initial
        begin
                bf_wraddr <= 0;
                bf_datnvol <= 0;
                bf_data <= 0;
                do_write <= 0;
                cur_st <= START;
        end



        // FSM!
        always @(posedge clock)
        begin
                if( dac_load )
                        cur_st <= START;
                else
                        cur_st <= nxt_st;
        end

        always @*
        begin
                case( cur_st )
/////////////////////////////////////////////////////////////////////
                START:
                        nxt_st <= LOAD_VOL;
/////////////////////////////////////////////////////////////////////
                LOAD_VOL:
                        nxt_st <= LOAD_VOL2;
/////////////////////////////////////////////////////////////////////
                LOAD_VOL2:
                        nxt_st <= LOAD_DAT;
/////////////////////////////////////////////////////////////////////
                LOAD_DAT:
                        nxt_st <= LOAD_DAT2;
/////////////////////////////////////////////////////////////////////
                LOAD_DAT2:
                        nxt_st <= START_MUL;
/////////////////////////////////////////////////////////////////////
                START_MUL:
                        nxt_st <= WAIT_STOP;
/////////////////////////////////////////////////////////////////////
                WAIT_STOP:
                        if( (!mulacc_ready) || (chanptr == 2'd3) )
                                nxt_st <= WAIT_STOP;
                        else
                                nxt_st <= LOOP;
/////////////////////////////////////////////////////////////////////
                LOOP:
                        nxt_st <= LOAD_VOL;
/////////////////////////////////////////////////////////////////////
                endcase
        end


        always @(posedge clock)
        begin
                case( cur_st )
/////////////////////////////////////////////////////////////////////
                START:
                begin
                        chanptr <= 2'd0;
                        mulacc_load <= 1'b0;
                        mem_read <= 1'b0;
                end
/////////////////////////////////////////////////////////////////////
                LOAD_VOL:
                begin
                        mem_read <= 1'b1;
                        datnvol <= 1'b0;
                end
/////////////////////////////////////////////////////////////////////
                LOAD_VOL2:
                begin
                        mem_read <= 1'b0;
                end
/////////////////////////////////////////////////////////////////////
                LOAD_DAT:
                begin
                        vol <= mem_do[5:0];
                        mem_read <= 1'b1;
                        datnvol <= 1'b1;
                end
/////////////////////////////////////////////////////////////////////
                LOAD_DAT2:
                begin
                        mem_read <= 1'b0;
                        mulacc_load <= 1'b1;
                end
/////////////////////////////////////////////////////////////////////
                START_MUL:
                begin
                        mulacc_load <= 1'b0;
                end
/////////////////////////////////////////////////////////////////////
//              WAIT_STOP:
/////////////////////////////////////////////////////////////////////
                LOOP:
                begin
                        chanptr <= chanptr + 2'd1;
                end
/////////////////////////////////////////////////////////////////////
                endcase
        end









// controlling writes to memory

        // toggles
        always @(negedge clock)
                wrtgl1 <= in_wrtoggle;
        always @(posedge clock)
                {wrtgl3,wrtgl2} <= {wrtgl2,wrtgl1};


        // intermediate buffers and writing
        always @(posedge clock)
        begin

                if( wrtgl3!=wrtgl2 )
                begin
                        bf_wraddr  <= in_wraddr;
                        bf_data    <= in_data;
                        bf_datnvol <= in_datnvol;

                        do_write <= 1'b1;
                end

                else if( mem_we )
                begin
                        do_write <= 1'b0;
                end

        end

        always @*
        begin
                mem_we <= do_write & (~mem_read);
        end


endmodule