1
0
mirror of https://github.com/Gehstock/Mist_FPGA.git synced 2026-02-18 05:23:45 +00:00
Files
Gehstock.Mist_FPGA/common/Sound/opl3/opl3.sv
2020-05-16 06:42:10 +02:00

187 lines
5.5 KiB
Systemverilog

/*
* 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 opl3
#(
parameter OPLCLK = 64000000 // opl_clk in Hz
)
(
input clk,
input clk_opl,
input rst_n,
output reg irq_n,
input [12:0] period_80us, // from clk
input [1:0] addr,
output [7:0] dout,
input [7:0] din,
input we,
output signed [15:0] sample_l,
output signed [15:0] sample_r
);
//------------------------------------------------------------------------------
wire [7:0] io_readdata = { timer1_overflow | timer2_overflow, timer1_overflow, timer2_overflow, 5'd0 };
assign dout = !addr ? io_readdata : 8'hFF;
//------------------------------------------------------------------------------
reg old_write;
always @(posedge clk) old_write <= we;
wire write = (~old_write & we);
reg [8:0] index;
always @(posedge clk or negedge rst_n) begin
if(rst_n == 0) index <= 0;
else if(~addr[0] && write) index <= {addr[1], din};
end
wire io_write = (addr[0] && write);
wire [7:0] io_writedata = din;
//------------------------------------------------------------------------------ timer 1
reg [7:0] timer1_preset;
always @(posedge clk or negedge rst_n) begin
if(rst_n == 0) timer1_preset <= 0;
else if(io_write && index == 2) timer1_preset <= io_writedata;
end
reg timer1_mask;
reg timer1_active;
always @(posedge clk or negedge rst_n) begin
if(rst_n == 0) {timer1_mask, timer1_active} <= 0;
else if(io_write && index == 4 && ~io_writedata[7]) {timer1_mask, timer1_active} <= {io_writedata[6], io_writedata[0]};
end
wire timer1_pulse;
timer timer1( clk, period_80us, timer1_preset, timer1_active, timer1_pulse );
reg timer1_overflow;
always @(posedge clk or negedge rst_n) begin
if(rst_n == 0) timer1_overflow <= 0;
else begin
if(io_write && index == 4 && io_writedata[7]) timer1_overflow <= 0;
if(timer1_pulse) timer1_overflow <= 1;
end
end
//------------------------------------------------------------------------------ timer 2
reg [7:0] timer2_preset;
always @(posedge clk or negedge rst_n) begin
if(rst_n == 0) timer2_preset <= 0;
else if(io_write && index == 3) timer2_preset <= io_writedata;
end
reg timer2_mask;
reg timer2_active;
always @(posedge clk or negedge rst_n) begin
if(rst_n == 0) {timer2_mask, timer2_active} <= 0;
else if(io_write && index == 4 && ~io_writedata[7]) {timer2_mask, timer2_active} <= {io_writedata[5], io_writedata[1]};
end
wire timer2_pulse;
timer timer2( clk, {period_80us, 2'b00}, timer2_preset, timer2_active, timer2_pulse );
reg timer2_overflow;
always @(posedge clk or negedge rst_n) begin
if(rst_n == 0) timer2_overflow <= 0;
else begin
if(io_write && index == 4 && io_writedata[7]) timer2_overflow <= 0;
if(timer2_pulse) timer2_overflow <= 1;
end
end
//------------------------------------------------------------------------------ IRQ
always @(posedge clk or negedge rst_n) begin
if(rst_n == 0) irq_n <= 1;
else begin
if(io_write && index == 4 && io_writedata[7]) irq_n <= 1;
if(~timer1_mask && timer1_pulse) irq_n <= 0;
if(~timer2_mask && timer2_pulse) irq_n <= 0;
end
end
opl3sw #(OPLCLK) opl3
(
.reset(~rst_n),
.cpu_clk(clk),
.addr(addr),
.din(din),
.wr(write),
.clk(clk_opl),
.left(sample_l),
.right(sample_r)
);
endmodule
module timer
(
input clk,
input [14:0] resolution,
input [7:0] init,
input active,
output reg overflow_pulse
);
always @(posedge clk) begin
reg [7:0] counter = 0;
reg [14:0] sub_counter = 0;
reg old_act;
old_act <= active;
overflow_pulse <= 0;
if(~old_act && active) begin
counter <= init;
sub_counter <= resolution;
end
else if(active) begin
sub_counter <= sub_counter - 1'd1;
if(!sub_counter) begin
sub_counter <= resolution;
counter <= counter + 1'd1;
if(&counter) begin
overflow_pulse <= 1;
counter <= init;
end
end
end
end
endmodule