OpenCores
URL https://opencores.org/ocsvn/ao486/ao486/trunk

Subversion Repositories ao486

[/] [ao486/] [trunk/] [rtl/] [soc/] [sound/] [sound_dsp.v] - Rev 2

Compare with Previous | Blame | View Log

/*
 * Copyright (c) 2014, Aleksander Osman
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * * Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 * 
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
module sound_dsp(
    input               clk,
    input               rst_n,
 
    output reg          irq,
 
    //io slave 220h-22Fh
    input       [3:0]   io_address,
    input               io_read,
    output      [7:0]   io_readdata_from_dsp,
    input               io_write,
    input       [7:0]   io_writedata,
 
    //dma
    output              dma_soundblaster_req,
    input               dma_soundblaster_ack,
    input               dma_soundblaster_terminal,
    input       [7:0]   dma_soundblaster_readdata,
    output      [7:0]   dma_soundblaster_writedata,
 
    //sample
    output              sample_from_dsp_disabled,
    output              sample_from_dsp_do,
    output      [7:0]   sample_from_dsp_value,
 
    //mgmt slave
    /*
    0-255.[15:0]: cycles in period
    */
    input       [8:0]   mgmt_address,
    input               mgmt_write,
    input       [31:0]  mgmt_writedata
);
 
//------------------------------------------------------------------------------
 
reg io_read_last;
always @(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) io_read_last <= 1'b0; else if(io_read_last) io_read_last <= 1'b0; else io_read_last <= io_read; end 
wire io_read_valid = io_read && io_read_last == 1'b0;
 
//------------------------------------------------------------------------------
 
assign io_readdata_from_dsp =
    (io_address == 4'hE)?                       { read_ready, 7'h7F } :
    (io_address == 4'hA && copy_cnt > 6'd0)?    copyright_byte :
    (io_address == 4'hA)?                       read_buffer[15:8] :
    (io_address == 4'hC)?                       { write_ready, 7'h7F } :
                                                8'hFF;
 
//------------------------------------------------------------------------------
 
wire highspeed_start = cmd_high_auto_dma_out || cmd_high_single_dma_out || cmd_high_auto_dma_input || cmd_high_single_dma_input;
 
reg highspeed_mode;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)           highspeed_mode <= 1'b0;
    else if(highspeed_reset)    highspeed_mode <= 1'b0;
    else if(highspeed_start)    highspeed_mode <= 1'b1;
    else if(dma_finished)       highspeed_mode <= 1'b0;
end
 
reg midi_uart_mode;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)           midi_uart_mode <= 1'b0;
    else if(midi_uart_reset)    midi_uart_mode <= 1'b0;
    else if(cmd_midi_uart)      midi_uart_mode <= 1'b1;
end
 
reg reset_reg;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)                                               reset_reg <= 1'b0;
    else if(io_write && io_address == 4'h6 && ~(highspeed_mode))    reset_reg <= io_writedata[0];
end
 
wire highspeed_reset = io_write && io_address == 4'h6 && highspeed_mode;
wire midi_uart_reset = reset_reg && io_write && io_address == 4'h6 && ~(highspeed_mode) && midi_uart_mode    && io_writedata[0] == 1'b0;
wire sw_reset        = reset_reg && io_write && io_address == 4'h6 && ~(highspeed_mode) && ~(midi_uart_mode) && io_writedata[0] == 1'b0;
 
//------------------------------------------------------------------------------ dummy input
 
wire input_strobe = cmd_direct_input || dma_input;
 
reg input_direction;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)                                                       input_direction <= 1'b0;
    else if(sw_reset)                                                       input_direction <= 1'b0;
    else if(input_strobe && ~(input_direction) && input_sample == 8'd254)   input_direction <= 1'b1;
    else if(input_strobe && input_direction    && input_sample == 8'd1)     input_direction <= 1'b0;
end
 
reg [7:0] input_sample;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)                           input_sample <= 8'd128;
    else if(sw_reset)                           input_sample <= 8'd128;
    else if(input_strobe && ~(input_direction)) input_sample <= input_sample + 8'd1;
    else if(input_strobe && input_direction)    input_sample <= input_sample - 8'd1;
end
 
assign dma_soundblaster_writedata = (dma_id_active)? dma_id_value : input_sample;
 
//------------------------------------------------------------------------------
 
wire cmd_single_byte = write_left == 2'd0 && io_write && io_address == 4'hC && ~(midi_uart_mode) && ~(highspeed_mode);
 
wire cmd_wait_for_2byte = write_left == 2'd0 && io_write && io_address == 4'hC && ~(midi_uart_mode) && ~(highspeed_mode) && (
    io_writedata == 8'h10 || //direct output
    io_writedata == 8'h38 || //midi   output
    io_writedata == 8'h40 || //set time constant
    io_writedata == 8'hE0 || //dsp identification
    io_writedata == 8'hE4 || //write test register
    io_writedata == 8'hE2    //dma id
);
wire cmd_wait_for_3byte = write_left == 2'd0 && io_write && io_address == 4'hC && ~(midi_uart_mode) && ~(highspeed_mode) && (
    io_writedata == 8'h14 ||   //single cycle dma output
    io_writedata == 8'h16 ||   //single cycle dma 2 bit adpcm output
    io_writedata == 8'h17 ||   //single cycle dma 2 bit adpcm output with reference
    io_writedata == 8'h74 ||   //single cycle dma 4 bit adpcm output
    io_writedata == 8'h75 ||   //single cycle dma 4 bit adpcm output with reference
    io_writedata == 8'h76 ||   //single cycle dma 4 bit adpcm output
    io_writedata == 8'h77 ||   //single cycle dma 4 bit adpcm output with reference
    io_writedata == 8'h24 ||   //single cycle dma input
    io_writedata == 8'h48 ||   //set block size
    io_writedata == 8'h80      //pause dac
);
 
wire cmd_multiple_byte = write_length > 2'd0 && write_left == 2'd0 && ~(midi_uart_mode) && ~(highspeed_mode);
 
wire cmd_dsp_version            = cmd_single_byte && io_writedata == 8'hE1;
wire cmd_direct_output          = cmd_multiple_byte && write_length == 2'd2 && write_buffer[15:8] == 8'h10;
wire cmd_single_dma_output      = cmd_multiple_byte && write_length == 2'd3 && write_buffer[23:16] == 8'h14;
wire cmd_single_2_adpcm_out     = cmd_multiple_byte && write_length == 2'd3 && write_buffer[23:16] == 8'h16;
wire cmd_single_2_adpcm_out_ref = cmd_multiple_byte && write_length == 2'd3 && write_buffer[23:16] == 8'h17;
wire cmd_single_4_adpcm_out     = cmd_multiple_byte && write_length == 2'd3 && write_buffer[23:16] == 8'h74;
wire cmd_single_4_adpcm_out_ref = cmd_multiple_byte && write_length == 2'd3 && write_buffer[23:16] == 8'h75;
wire cmd_single_3_adpcm_out     = cmd_multiple_byte && write_length == 2'd3 && write_buffer[23:16] == 8'h76;
wire cmd_single_3_adpcm_out_ref = cmd_multiple_byte && write_length == 2'd3 && write_buffer[23:16] == 8'h77;
 
wire cmd_single_dma_input       = cmd_multiple_byte && write_length == 2'd3 && write_buffer[23:16] == 8'h24;
 
wire cmd_auto_dma_out           = cmd_single_byte && io_writedata == 8'h1C;
wire cmd_auto_dma_exit          = cmd_single_byte && io_writedata == 8'hDA;
 
wire cmd_auto_2_adpcm_out_ref   = cmd_single_byte && io_writedata == 8'h1F;
wire cmd_auto_3_adpcm_out_ref   = cmd_single_byte && io_writedata == 8'h7F;
wire cmd_auto_4_adpcm_out_ref   = cmd_single_byte && io_writedata == 8'h7D;
 
wire cmd_direct_input           = cmd_single_byte && io_writedata == 8'h20;
wire cmd_auto_dma_input         = cmd_single_byte && io_writedata == 8'h2C;
 
//wire cmd_midi_polling_input     = cmd_single_byte && io_writedata == 8'h30;
//wire cmd_midi_interrupt_input   = cmd_single_byte && io_writedata == 8'h31;
 
wire cmd_midi_uart              = cmd_single_byte && { io_writedata[7:2], 2'b00 } == 8'h34;
//wire cmd_midi_output            = cmd_multiple_byte && write_length == 2'd2 && write_buffer[15:8] == 8'h38;
 
wire cmd_set_time_constant      = cmd_multiple_byte && write_length == 2'd2 && write_buffer[15:8] == 8'h40;
wire cmd_set_block_size         = cmd_multiple_byte && write_length == 2'd3 && write_buffer[23:16] == 8'h48;
 
wire cmd_pause_dac              = cmd_multiple_byte && write_length == 2'd3 && write_buffer[23:16] == 8'h80;
 
wire cmd_high_auto_dma_out      = cmd_single_byte && io_writedata == 8'h90;
wire cmd_high_single_dma_out    = cmd_single_byte && io_writedata == 8'h91;
 
wire cmd_high_auto_dma_input    = cmd_single_byte && io_writedata == 8'h98;
wire cmd_high_single_dma_input  = cmd_single_byte && io_writedata == 8'h99;
 
wire cmd_dma_pause_start        = cmd_single_byte && io_writedata == 8'hD0;
wire cmd_dma_pause_end          = cmd_single_byte && io_writedata == 8'hD4;
 
wire cmd_speaker_on             = cmd_single_byte && io_writedata == 8'hD1;
wire cmd_speaker_off            = cmd_single_byte && io_writedata == 8'hD3;
wire cmd_speaker_status         = cmd_single_byte && io_writedata == 8'hD8;
 
wire cmd_dsp_identification     = cmd_multiple_byte && write_length == 2'd2 && write_buffer[15:8] == 8'hE0;
wire cmd_f8_zero                = cmd_single_byte && io_writedata == 8'hF8;
wire cmd_trigger_irq            = cmd_single_byte && io_writedata == 8'hF2;
 
wire cmd_test_register_write    = cmd_multiple_byte && write_length == 2'd2 && write_buffer[15:8] == 8'hE4;
wire cmd_test_register_read     = cmd_single_byte && io_writedata == 8'hE8;
 
wire cmd_copyright              = cmd_single_byte && io_writedata == 8'hE3;
wire cmd_dma_id                 = cmd_multiple_byte && write_length == 2'd2 && write_buffer[15:8] == 8'hE2;
 
//------------------------------------------------------------------------------ 'weird dma identification' from DosBox
 
reg [1:0] dma_id_count;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)   dma_id_count <= 2'd0;
    else if(sw_reset)   dma_id_count <= 2'd0;
    else if(cmd_dma_id) dma_id_count <= dma_id_count + 2'd1;
end
 
reg [7:0] dma_id_value;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)   dma_id_value <= 8'hAA;
    else if(sw_reset)   dma_id_value <= 8'hAA;
    else if(cmd_dma_id) dma_id_value <= dma_id_value + dma_id_q;
end
 
reg dma_id_active;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)               dma_id_active <= 1'b0;
    else if(sw_reset)               dma_id_active <= 1'b0;
    else if(cmd_dma_id)             dma_id_active <= 1'b1;
    else if(dma_soundblaster_ack)   dma_id_active <= 1'b0;
end
 
wire [7:0] dma_id_q;
 
simple_single_rom #(
    .widthad    (10),
    .width      (8),
    .datafile   ("./../soc/sound/dsp_dma_identification_rom.hex")
)
dma_id_rom_inst (
    .clk        (clk),
 
    .addr       ({dma_id_count, io_writedata}),
    .q          (dma_id_q)
);
 
//------------------------------------------------------------------------------ copyright
 
reg [5:0] copy_cnt;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)                                               copy_cnt <= 6'd0;
    else if(cmd_copyright)                                          copy_cnt <= 6'd45;
    else if(io_read_valid && io_address == 4'hA && copy_cnt > 6'd0) copy_cnt <= copy_cnt - 6'd1;
end
 
wire [7:0] copyright_byte =
    (copy_cnt == 6'd45 || copy_cnt == 6'd34 || copy_cnt == 6'd31 || copy_cnt == 6'd20)?                     8'h43 : //C
    (copy_cnt == 6'd44 || copy_cnt == 6'd17 || copy_cnt == 6'd15)?                                          8'h4F : //O
    (copy_cnt == 6'd43)?                                                                                    8'h50 : //P
    (copy_cnt == 6'd42 || copy_cnt == 6'd13)?                                                               8'h59 : //Y
    (copy_cnt == 6'd41 || copy_cnt == 6'd30)?                                                               8'h52 : //R
    (copy_cnt == 6'd40 || copy_cnt == 6'd26)?                                                               8'h49 : //I
    (copy_cnt == 6'd39 || copy_cnt == 6'd14)?                                                               8'h47 : //G
    (copy_cnt == 6'd38 || copy_cnt == 6'd19)?                                                               8'h48 : //H
    (copy_cnt == 6'd37 || copy_cnt == 6'd27 || copy_cnt == 6'd22 || copy_cnt == 6'd10)?                     8'h54 : //T
    (copy_cnt == 6'd36 || copy_cnt == 6'd32 || copy_cnt == 6'd23 || copy_cnt == 6'd12 || copy_cnt == 6'd7)? 8'h20 : //' '
    (copy_cnt == 6'd35)?                                                                                    8'h28 : //(
    (copy_cnt == 6'd33)?                                                                                    8'h29 : //)
    (copy_cnt == 6'd29 || copy_cnt == 6'd24 || copy_cnt == 6'd21)?                                          8'h45 : //E
    (copy_cnt == 6'd28)?                                                                                    8'h41 : //A
    (copy_cnt == 6'd25)?                                                                                    8'h56 : //V
    (copy_cnt == 6'd18)?                                                                                    8'h4E : //N
    (copy_cnt == 6'd16 || copy_cnt == 6'd11)?                                                               8'h4C : //L
    (copy_cnt == 6'd13)?                                                                                    8'h59 : //Y
    (copy_cnt == 6'd9)?                                                                                     8'h44 : //D
    (copy_cnt == 6'd8)?                                                                                     8'h2C : //,
    (copy_cnt == 6'd6)?                                                                                     8'h31 : //1
    (copy_cnt == 6'd5 || copy_cnt == 6'd4)?                                                                 8'h39 : //9
    (copy_cnt == 6'd3)?                                                                                     8'h32 : //2
    (copy_cnt == 6'd2)?                                                                                     8'h2E : //.
                                                                                                            8'h00;  //for copy_cnt == 6'd1
//------------------------------------------------------------------------------
 
reg [7:0] test_register;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)                   test_register <= 8'd0;
    else if(cmd_test_register_write)    test_register <= write_buffer[7:0];
end
 
reg speaker_on;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)           speaker_on <= 1'b0;
    else if(sw_reset)           speaker_on <= 1'b0;
    else if(cmd_speaker_on)     speaker_on <= 1'b1;
    else if(cmd_speaker_off)    speaker_on <= 1'b0;
end
 
reg [15:0] block_size;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)           block_size <= 16'd0;
    else if(sw_reset)           block_size <= 16'd0;
    else if(cmd_set_block_size) block_size <= { write_buffer[7:0], write_buffer[15:8] };
end
 
reg pause_dma;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)                                                   pause_dma <= 1'b0;
    else if(sw_reset)                                                   pause_dma <= 1'b0;
    else if(cmd_dma_pause_start)                                        pause_dma <= 1'b1;
    else if(cmd_dma_pause_end || dma_single_start || dma_auto_start)    pause_dma <= 1'b0;
end
 
//------------------------------------------------------------------------------ pause dac
 
wire pause_interrupt = pause_counter == 16'd0 && pause_period == 16'd1;
 
reg pause_active;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)                                           pause_active <= 1'b0;
    else if(sw_reset)                                           pause_active <= 1'b0;
    else if(cmd_pause_dac)                                      pause_active <= 1'b1;
    else if(pause_counter == 16'd0 && pause_period == 16'd0)    pause_active <= 1'b0;
end
 
reg [15:0] pause_counter;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)                                       pause_counter <= 16'd0;
    else if(sw_reset)                                       pause_counter <= 16'd0;
    else if(cmd_pause_dac)                                  pause_counter <= { write_buffer[7:0], write_buffer[15:8] };
    else if(pause_period == 16'd0 && pause_counter > 16'd0) pause_counter <= pause_counter - 16'd1;
end
 
reg [15:0] pause_period;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)                                       pause_period <= 16'd0;
    else if(sw_reset)                                       pause_period <= 16'd0;
    else if(cmd_pause_dac)                                  pause_period <= period_q;
    else if(pause_period == 16'd0 && pause_counter > 16'd0) pause_period <= period_q;
    else if(pause_period > 16'd0)                           pause_period <= pause_period - 16'd1;
end
 
//------------------------------------------------------------------------------
 
wire write_ready = midi_uart_mode || highspeed_mode;
 
reg [1:0] write_length;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)                           write_length <= 2'd0;
    else if(sw_reset)                           write_length <= 2'd0;
    else if(cmd_midi_uart || highspeed_start)   write_length <= 2'd0;
    else if(cmd_wait_for_2byte)                 write_length <= 2'd2;
    else if(cmd_wait_for_3byte)                 write_length <= 2'd3;
    else if(write_left == 2'd0)                 write_length <= 2'd0;
end
 
reg [1:0] write_left;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)                                               write_left <= 2'd0;
    else if(sw_reset)                                               write_left <= 2'd0;
    else if(cmd_midi_uart || highspeed_start)                       write_left <= 2'd0;
    else if(cmd_wait_for_2byte)                                     write_left <= 2'd1;
    else if(cmd_wait_for_3byte)                                     write_left <= 2'd2;
    else if(io_write && io_address == 4'hC && write_left > 2'd0)    write_left <= write_left - 2'd1;
end
 
reg [23:0] write_buffer;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)                                               write_buffer <= 24'd0;
    else if(sw_reset)                                               write_buffer <= 24'd0;
    else if(cmd_wait_for_2byte || cmd_wait_for_3byte)               write_buffer <= { write_buffer[15:0], io_writedata };
    else if(io_write && io_address == 4'hC && write_left > 2'd0)    write_buffer <= { write_buffer[15:0], io_writedata };
end
 
//------------------------------------------------------------------------------
 
wire read_ready = ~(midi_uart_mode) && read_buffer_size > 2'd0;
 
reg [1:0] read_buffer_size;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)                                                   read_buffer_size <= 2'd0;
    else if(sw_reset)                                                   read_buffer_size <= 2'd1;
    else if(cmd_dsp_version)                                            read_buffer_size <= 2'd2;
    else if(cmd_direct_input)                                           read_buffer_size <= 2'd1;
    else if(cmd_midi_uart)                                              read_buffer_size <= 2'd0;
    else if(cmd_speaker_status)                                         read_buffer_size <= 2'd1;
    else if(cmd_dsp_identification)                                     read_buffer_size <= 2'd1;
    else if(cmd_f8_zero)                                                read_buffer_size <= 2'd1;
    else if(cmd_test_register_read)                                     read_buffer_size <= 2'd1;
    else if(cmd_copyright)                                              read_buffer_size <= 2'd0;
 
    else if(io_read_valid && io_address == 4'hA && read_buffer_size > 2'd0) read_buffer_size <= read_buffer_size - 2'd1;
end
 
reg [15:0] read_buffer;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)                                                   read_buffer <= 16'd0;
    else if(sw_reset)                                                   read_buffer <= { 8'hAA, 8'h00 };
    else if(cmd_dsp_version)                                            read_buffer <= { 8'h02, 8'h01 };
    else if(cmd_direct_input)                                           read_buffer <= { input_sample, 8'h00 };
    else if(cmd_speaker_status)                                         read_buffer <= { (speaker_on)? 8'hFF : 8'h00, 8'h00 };
    else if(cmd_dsp_identification)                                     read_buffer <= { ~(write_buffer[7:0]), 8'h00 };
    else if(cmd_f8_zero)                                                read_buffer <= { 8'h00, 8'h00 };
    else if(cmd_test_register_read)                                     read_buffer <= { test_register, 8'h00 };
 
    else if(io_read_valid && io_address == 4'hA && read_buffer_size > 2'd0) read_buffer <= { read_buffer[7:0], 8'd0 };
end
 
//------------------------------------------------------------------------------ irq
 
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)                                               irq <= 1'b0;
    else if(sw_reset)                                               irq <= 1'b0;
 
    else if(dma_finished || dma_auto_restart || pause_interrupt)    irq <= 1'b1;
    else if(cmd_trigger_irq)                                        irq <= 1'b1;
 
    else if(io_read_valid && io_address == 4'hE)                    irq <= 1'b0;
end
 
//------------------------------------------------------------------------------ dma commands
 
localparam [4:0] S_IDLE                 = 5'd0;
localparam [4:0] S_OUT_SINGLE_8_BIT     = 5'd1;
localparam [4:0] S_OUT_SINGLE_4_BIT     = 5'd2;
localparam [4:0] S_OUT_SINGLE_3_BIT     = 5'd3;
localparam [4:0] S_OUT_SINGLE_2_BIT     = 5'd4;
localparam [4:0] S_OUT_SINGLE_4_BIT_REF = 5'd5;
localparam [4:0] S_OUT_SINGLE_3_BIT_REF = 5'd6;
localparam [4:0] S_OUT_SINGLE_2_BIT_REF = 5'd7;
localparam [4:0] S_OUT_AUTO_8_BIT       = 5'd8;
localparam [4:0] S_OUT_AUTO_4_BIT_REF   = 5'd9;
localparam [4:0] S_OUT_AUTO_3_BIT_REF   = 5'd10;
localparam [4:0] S_OUT_AUTO_2_BIT_REF   = 5'd11;
localparam [4:0] S_IN_SINGLE            = 5'd12;
localparam [4:0] S_IN_AUTO              = 5'd13;
localparam [4:0] S_OUT_SINGLE_HIGH      = 5'd14;
localparam [4:0] S_OUT_AUTO_HIGH        = 5'd15;
localparam [4:0] S_IN_SINGLE_HIGH       = 5'd16;
localparam [4:0] S_IN_AUTO_HIGH         = 5'd17;
 
reg [4:0] dma_command;
 
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)                           dma_command <= S_IDLE;
    else if(sw_reset)                           dma_command <= S_IDLE;
 
    else if(cmd_single_dma_output)              dma_command <= S_OUT_SINGLE_8_BIT;
    else if(cmd_single_4_adpcm_out)             dma_command <= S_OUT_SINGLE_4_BIT;
    else if(cmd_single_3_adpcm_out)             dma_command <= S_OUT_SINGLE_3_BIT;
    else if(cmd_single_2_adpcm_out)             dma_command <= S_OUT_SINGLE_2_BIT;
 
    else if(cmd_single_4_adpcm_out_ref)         dma_command <= S_OUT_SINGLE_4_BIT_REF;
    else if(cmd_single_3_adpcm_out_ref)         dma_command <= S_OUT_SINGLE_3_BIT_REF;
    else if(cmd_single_2_adpcm_out_ref)         dma_command <= S_OUT_SINGLE_2_BIT_REF;
 
    else if(cmd_auto_dma_out)                   dma_command <= S_OUT_AUTO_8_BIT;
    else if(cmd_auto_4_adpcm_out_ref)           dma_command <= S_OUT_AUTO_4_BIT_REF;
    else if(cmd_auto_3_adpcm_out_ref)           dma_command <= S_OUT_AUTO_3_BIT_REF;
    else if(cmd_auto_2_adpcm_out_ref)           dma_command <= S_OUT_AUTO_2_BIT_REF;
 
    else if(cmd_single_dma_input)               dma_command <= S_IN_SINGLE;
    else if(cmd_auto_dma_input)                 dma_command <= S_IN_AUTO;
 
    else if(cmd_high_single_dma_out)            dma_command <= S_OUT_SINGLE_HIGH;
    else if(cmd_high_auto_dma_out)              dma_command <= S_OUT_AUTO_HIGH;
 
    else if(cmd_high_single_dma_input)          dma_command <= S_IN_SINGLE_HIGH;
    else if(cmd_high_auto_dma_input)            dma_command <= S_IN_AUTO_HIGH;
 
    else if(dma_single_start || dma_auto_start) dma_command <= S_IDLE;
end
 
//------------------------------------------------------------------------------ dma
 
wire dma_single_start = dma_restart_possible && (
    dma_command == S_OUT_SINGLE_8_BIT || dma_command == S_OUT_SINGLE_4_BIT     || dma_command == S_OUT_SINGLE_3_BIT     || dma_command == S_OUT_SINGLE_2_BIT     ||
                                         dma_command == S_OUT_SINGLE_4_BIT_REF || dma_command == S_OUT_SINGLE_3_BIT_REF || dma_command == S_OUT_SINGLE_2_BIT_REF ||
    dma_command == S_IN_SINGLE ||
    dma_command == S_OUT_SINGLE_HIGH || dma_command == S_IN_SINGLE_HIGH
);
 
wire dma_auto_start = dma_restart_possible && (
    dma_command == S_OUT_AUTO_8_BIT || dma_command == S_OUT_AUTO_4_BIT_REF || dma_command == S_OUT_AUTO_3_BIT_REF || dma_command == S_OUT_AUTO_2_BIT_REF ||
    dma_command == S_IN_AUTO ||
    dma_command == S_OUT_AUTO_HIGH || dma_command == S_IN_AUTO_HIGH
);
 
wire dma_normal_req = dma_in_progress && dma_wait == 16'd0 && adpcm_wait == 2'd0 && ~(pause_dma);
 
assign dma_soundblaster_req = dma_id_active || dma_normal_req;
 
wire dma_valid  = dma_normal_req && dma_soundblaster_ack && ~(dma_id_active);
wire dma_output = ~(dma_is_input) && dma_valid;
wire dma_input  = dma_is_input    && dma_valid;
 
wire dma_finished = dma_in_progress && ~(dma_autoinit) && (
    (dma_valid && dma_left == 17'd1 && adpcm_type == ADPCM_NONE) ||
    (adpcm_output && dma_left == 17'd0 && adpcm_type != ADPCM_NONE && adpcm_wait == 2'd1)
);
 
wire dma_auto_restart = dma_in_progress && dma_autoinit && (
    (dma_valid && dma_left == 17'd1 && adpcm_type == ADPCM_NONE) ||
    (adpcm_output && dma_left == 17'd0 && adpcm_type != ADPCM_NONE && adpcm_wait == 2'd1)
);
 
wire dma_restart_possible = dma_wait == 16'd0 && (adpcm_wait == 2'd0 || (adpcm_type != ADPCM_NONE && adpcm_wait == 2'd1)) && (~(dma_in_progress) || dma_auto_restart || pause_dma);
 
reg [16:0] dma_left;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)                           dma_left <= 17'd0;
    else if(sw_reset)                           dma_left <= 17'd0;
    else if(dma_single_start)                   dma_left <= { write_buffer[7:0], write_buffer[15:8] } + 16'd1;
    else if(dma_auto_start || dma_auto_restart) dma_left <= block_size + 16'd1;
    else if(dma_valid && dma_left > 17'd0)      dma_left <= dma_left - 17'd1;
    else if(dma_finished)                       dma_left <= 17'd0;
end
 
reg dma_in_progress;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)                           dma_in_progress <= 1'b0;
    else if(sw_reset)                           dma_in_progress <= 1'b0;
    else if(dma_single_start || dma_auto_start) dma_in_progress <= 1'b1;
    else if(dma_finished)                       dma_in_progress <= 1'b0;
end
 
reg dma_is_input;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)                                                                                                                                                               dma_is_input <= 1'b0;
    else if(sw_reset)                                                                                                                                                               dma_is_input <= 1'b0;
    else if((dma_single_start || dma_auto_start) && (dma_command == S_IN_SINGLE || dma_command == S_IN_AUTO || dma_command == S_IN_SINGLE_HIGH || dma_command == S_IN_AUTO_HIGH))   dma_is_input <= 1'b1;
    else if(dma_single_start || dma_auto_start)                                                                                                                                     dma_is_input <= 1'b0;
end
 
reg dma_autoinit;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)           dma_autoinit <= 1'b0;
    else if(sw_reset)           dma_autoinit <= 1'b0;
    else if(dma_single_start)   dma_autoinit <= 1'b0;
    else if(dma_auto_start)     dma_autoinit <= 1'b1;
    else if(cmd_auto_dma_exit)  dma_autoinit <= 1'b0;
end
 
reg [15:0] dma_wait;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)                                                                                                   dma_wait <= 16'd0;
    else if(sw_reset)                                                                                                   dma_wait <= 16'd0;
    else if(dma_finished || dma_valid || adpcm_output || (~(dma_in_progress) && (dma_single_start || dma_auto_start)))  dma_wait <= period_q;
    else if(dma_wait > 16'd0)                                                                                           dma_wait <= dma_wait - 16'd1;
end
 
//------------------------------------------------------------------------------ adpcm
 
localparam [1:0] ADPCM_NONE = 2'd0;
localparam [1:0] ADPCM_4BIT = 2'd1;
localparam [1:0] ADPCM_3BIT = 2'd2;
localparam [1:0] ADPCM_2BIT = 2'd3;
 
wire adpcm_reference_start = 
    (dma_single_start || dma_auto_start) && (
    dma_command == S_OUT_SINGLE_2_BIT_REF || dma_command == S_OUT_SINGLE_3_BIT_REF || dma_command == S_OUT_SINGLE_4_BIT_REF ||
    dma_command == S_OUT_AUTO_2_BIT_REF   || dma_command == S_OUT_AUTO_3_BIT_REF   || dma_command == S_OUT_AUTO_4_BIT_REF
);
 
wire adpcm_output = dma_wait == 16'd0 && adpcm_wait > 2'd0;
 
reg adpcm_reference_awaiting;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)                               adpcm_reference_awaiting <= 1'b0;
    else if(sw_reset)                               adpcm_reference_awaiting <= 1'b0;
    else if(adpcm_reference_start)                  adpcm_reference_awaiting <= 1'b1;
    else if(dma_single_start || dma_auto_start)     adpcm_reference_awaiting <= 1'b0;
    else if(adpcm_reference_awaiting && dma_output) adpcm_reference_awaiting <= 1'b0;
    else if(dma_finished)                           adpcm_reference_awaiting <= 1'b0;
end
 
reg adpcm_reference_output;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)   adpcm_reference_output <= 1'b0;
    else                adpcm_reference_output <= adpcm_reference_awaiting;
end
 
reg [1:0] adpcm_wait;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)                               adpcm_wait <= 2'd0;
    else if(sw_reset)                               adpcm_wait <= 2'd0;
    else if(dma_single_start || dma_auto_start)     adpcm_wait <= 2'd0;
    else if(adpcm_reference_awaiting && dma_output) adpcm_wait <= 2'd0;
    else if(dma_output && adpcm_type == ADPCM_2BIT) adpcm_wait <= 2'd3;
    else if(dma_output && adpcm_type == ADPCM_3BIT) adpcm_wait <= 2'd2;
    else if(dma_output && adpcm_type == ADPCM_4BIT) adpcm_wait <= 2'd1;
    else if(adpcm_output && adpcm_wait > 2'd0)      adpcm_wait <= adpcm_wait - 2'd1;
end
 
reg [7:0] adpcm_sample;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)                                   adpcm_sample <= 8'd0;
    else if(sw_reset)                                   adpcm_sample <= 8'd0;
    else if(dma_output)                                 adpcm_sample <= dma_soundblaster_readdata;
    else if(adpcm_output && adpcm_type == ADPCM_2BIT)   adpcm_sample <= { adpcm_sample[5:0], 2'b0 };
    else if(adpcm_output && adpcm_type == ADPCM_3BIT)   adpcm_sample <= { adpcm_sample[4:0], 3'b0 };
    else if(adpcm_output && adpcm_type == ADPCM_4BIT)   adpcm_sample <= { adpcm_sample[3:0], 4'b0 };
end
 
reg adpcm_active;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)                                   adpcm_active <= 1'b0;
    else if(sw_reset)                                   adpcm_active <= 1'b0;
    else if(adpcm_reference_awaiting && dma_output)     adpcm_active <= 1'b0;
    else if(adpcm_type != ADPCM_NONE && dma_output)     adpcm_active <= 1'b1;
    else if(adpcm_output)                               adpcm_active <= 1'b1;
    else                                                adpcm_active <= 1'b0;
end
 
wire [7:0] adpcm_active_value =
    (adpcm_type == ADPCM_2BIT)?     adpcm_2bit_reference_next :
    (adpcm_type == ADPCM_3BIT)?     adpcm_3bit_reference_next :
                                    adpcm_4bit_reference_next;
 
reg [1:0] adpcm_type;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)                                                                                                                                                       adpcm_type <= ADPCM_NONE;
    else if(sw_reset)                                                                                                                                                       adpcm_type <= ADPCM_NONE;
    else if((dma_single_start || dma_auto_start) && (dma_command == S_OUT_SINGLE_2_BIT_REF || dma_command == S_OUT_SINGLE_2_BIT || dma_command == S_OUT_AUTO_2_BIT_REF))    adpcm_type <= ADPCM_2BIT;
    else if((dma_single_start || dma_auto_start) && (dma_command == S_OUT_SINGLE_3_BIT_REF || dma_command == S_OUT_SINGLE_3_BIT || dma_command == S_OUT_AUTO_3_BIT_REF))    adpcm_type <= ADPCM_3BIT;
    else if((dma_single_start || dma_auto_start) && (dma_command == S_OUT_SINGLE_4_BIT_REF || dma_command == S_OUT_SINGLE_4_BIT || dma_command == S_OUT_AUTO_4_BIT_REF))    adpcm_type <= ADPCM_4BIT;
    else if((dma_single_start || dma_auto_start))                                                                                                                           adpcm_type <= ADPCM_NONE;
    else if(dma_finished)                                                                                                                                                   adpcm_type <= ADPCM_NONE;
end
 
reg [2:0] adpcm_step;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)                                   adpcm_step <= 3'd0;
    else if(sw_reset)                                   adpcm_step <= 3'd0;
    else if(adpcm_active && adpcm_type == ADPCM_2BIT)   adpcm_step <= adpcm_2bit_step_next;
    else if(adpcm_active && adpcm_type == ADPCM_3BIT)   adpcm_step <= adpcm_3bit_step_next;
    else if(adpcm_active && adpcm_type == ADPCM_4BIT)   adpcm_step <= adpcm_4bit_step_next;
    else if(adpcm_reference_awaiting && dma_output)     adpcm_step <= 3'd0;
end
 
reg [7:0] adpcm_reference;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)                                   adpcm_reference <= 8'd0;
    else if(sw_reset)                                   adpcm_reference <= 8'd0;
    else if(adpcm_active && adpcm_type == ADPCM_2BIT)   adpcm_reference <= adpcm_2bit_reference_next;
    else if(adpcm_active && adpcm_type == ADPCM_3BIT)   adpcm_reference <= adpcm_3bit_reference_next;
    else if(adpcm_active && adpcm_type == ADPCM_4BIT)   adpcm_reference <= adpcm_4bit_reference_next;
    else if(adpcm_reference_awaiting && dma_output)     adpcm_reference <= dma_soundblaster_readdata;
end
 
//------------------------------------------------------------------------------ adpcm 2 bit
 
wire [1:0] adpcm_2bit_sample = adpcm_sample[7:6];
 
wire [7:0] adpcm_2bit_reference_adjust =
    (adpcm_step[2:0] == 3'd0)?      { 7'd0, adpcm_2bit_sample[0] } :
    (adpcm_step[2:0] == 3'd1)?      { 6'd0, adpcm_2bit_sample[0], 1'b1 } :
    (adpcm_step[2:0] == 3'd2)?      { 5'd0, adpcm_2bit_sample[0], 2'b10 } :
    (adpcm_step[2:0] == 3'd3)?      { 4'd0, adpcm_2bit_sample[0], 3'b100 } :
    (adpcm_step[2:0] == 3'd4)?      { 3'd0, adpcm_2bit_sample[0], 4'b1000 } :
                                    { 2'd0, adpcm_2bit_sample[0], 5'b10000 }; //adpcm_step[2:0] == 3'd5
 
wire [8:0] adpcm_2bit_reference_sum = adpcm_reference + adpcm_2bit_reference_adjust;
wire [8:0] adpcm_2bit_reference_sub = adpcm_reference - adpcm_2bit_reference_adjust;
 
wire [7:0] adpcm_2bit_reference_next =
    (adpcm_2bit_sample[1] && adpcm_2bit_reference_sub[8])?  8'd0 :
    (adpcm_2bit_sample[1])?                                 adpcm_2bit_reference_sub[7:0] :
    (adpcm_2bit_reference_sum[8])?                          8'd255 :
                                                            adpcm_2bit_reference_sum[7:0];
wire [2:0] adpcm_2bit_step_next =
    (adpcm_step < 3'd5 && adpcm_2bit_sample[0] == 1'b1)?    adpcm_step + 3'd1 :
    (adpcm_step > 3'd0 && adpcm_2bit_sample[0] == 1'b0)?    adpcm_step - 3'd1 :
                                                            adpcm_step;
 
//------------------------------------------------------------------------------ adpcm 3 bit
 
wire [2:0] adpcm_3bit_sample = adpcm_sample[7:5];
 
wire [7:0] adpcm_3bit_reference_adjust =
    (adpcm_step[2:0] == 3'd0)?                                  { 6'd0, adpcm_3bit_sample[1:0] } :
    (adpcm_step[2:0] == 3'd1)?                                  { 5'd0, adpcm_3bit_sample[1:0], 1'b1 } :
    (adpcm_step[2:0] == 3'd2)?                                  { 4'd0, adpcm_3bit_sample[1:0], 2'b10 } :
    (adpcm_step[2:0] == 3'd3)?                                  { 3'd0, adpcm_3bit_sample[1:0], 3'b100 } :
    (adpcm_step[2:0] == 3'd4 && adpcm_3bit_sample == 3'd0)?     8'd5 :
    (adpcm_step[2:0] == 3'd4 && adpcm_3bit_sample == 3'd1)?     8'd15 :
    (adpcm_step[2:0] == 3'd4 && adpcm_3bit_sample == 3'd2)?     8'd25 :
                                                                8'd35;
 
wire [8:0] adpcm_3bit_reference_sum = adpcm_reference + adpcm_3bit_reference_adjust;
wire [8:0] adpcm_3bit_reference_sub = adpcm_reference - adpcm_3bit_reference_adjust;
 
wire [7:0] adpcm_3bit_reference_next =
    (adpcm_3bit_sample[2] && adpcm_3bit_reference_sub[8])?  8'd0 :
    (adpcm_3bit_sample[2])?                                 adpcm_3bit_reference_sub[7:0] :
    (adpcm_3bit_reference_sum[8])?                          8'd255 :
                                                            adpcm_3bit_reference_sum[7:0];
wire [2:0] adpcm_3bit_step_next =
    (adpcm_step < 3'd4 && adpcm_3bit_sample[1:0] == 2'b11)? adpcm_step + 3'd1 :
    (adpcm_step > 3'd0 && adpcm_3bit_sample[1:0] == 2'b00)? adpcm_step - 3'd1 :
                                                            adpcm_step;
 
//------------------------------------------------------------------------------ adpcm 4 bit
 
wire [3:0] adpcm_4bit_sample = adpcm_sample[7:4];
 
wire [7:0] adpcm_4bit_reference_adjust =
    (adpcm_step[2:0] == 3'd0)?      { 5'd0, adpcm_4bit_sample[2:0] } :
    (adpcm_step[2:0] == 3'd1)?      { 4'd0, adpcm_4bit_sample[2:0], 1'b1 } :
    (adpcm_step[2:0] == 3'd2)?      { 3'd0, adpcm_4bit_sample[2:0], 2'b10 } :
                                    { 2'd0, adpcm_4bit_sample[2:0], 3'b100 }; //adpcm_step[2:0] == 3'd3
 
wire [8:0] adpcm_4bit_reference_sum = adpcm_reference + adpcm_4bit_reference_adjust;
wire [8:0] adpcm_4bit_reference_sub = adpcm_reference - adpcm_4bit_reference_adjust;
 
wire [7:0] adpcm_4bit_reference_next =
    (adpcm_4bit_sample[3] && adpcm_4bit_reference_sub[8])?  8'd0 :
    (adpcm_4bit_sample[3])?                                 adpcm_4bit_reference_sub[7:0] :
    (adpcm_4bit_reference_sum[8])?                          8'd255 :
                                                            adpcm_4bit_reference_sum[7:0];
wire [2:0] adpcm_4bit_step_next =
    (adpcm_step < 3'd3 && adpcm_4bit_sample[2:0] >= 3'd5)?  adpcm_step + 3'd1 :
    (adpcm_step > 3'd0 && adpcm_4bit_sample[2:0] == 3'd0)?  adpcm_step - 3'd1 :
                                                            adpcm_step;
 
//------------------------------------------------------------------------------
 
reg [7:0] period_address;
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)               period_address <= 8'd128;
    else if(sw_reset)               period_address <= 8'd128;
    else if(cmd_set_time_constant)  period_address <= write_buffer[7:0];
end
 
wire [15:0] period_q;
 
simple_ram #(
    .widthad    (8),
    .width      (16)
)
period_ram_inst(
    .clk                (clk),
 
    .wraddress          (mgmt_address[7:0]),
    .wren               (mgmt_write && mgmt_address[8] == 1'b0),
    .data               (mgmt_writedata[15:0]),
 
    .rdaddress          (period_address),
    .q                  (period_q)
);
 
//------------------------------------------------------------------------------
 
assign sample_from_dsp_disabled = ~(speaker_on) || pause_active;
assign sample_from_dsp_do = ~(sample_from_dsp_disabled) && ((dma_output && adpcm_type == ADPCM_NONE) || cmd_direct_output || adpcm_active || (~(adpcm_reference_awaiting) && adpcm_reference_output));
 
assign sample_from_dsp_value =
    (adpcm_reference_output)?   adpcm_sample :
    (adpcm_active)?             adpcm_active_value :
    (dma_output)?               dma_soundblaster_readdata :
                                write_buffer[7:0];
 
//------------------------------------------------------------------------------
 
// synthesis translate_off
wire _unused_ok = &{ 1'b0, mgmt_writedata[31:16], dma_soundblaster_terminal, 1'b0 };
// synthesis translate_on
 
//------------------------------------------------------------------------------
 
endmodule
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.