Files
TSConf_MiST/rtl/sound/jt12/jt12_top.v
Eugene Lozovoy ba7d903e12 update JT12
2024-09-10 17:07:34 +03:00

698 lines
22 KiB
Verilog

/* This file is part of JT12.
JT12 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
JT12 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with JT12. If not, see <http://www.gnu.org/licenses/>.
Author: Jose Tejada Gomez. Twitter: @topapate
Version: 1.0
Date: 14-2-2016
Based on information posted by Nemesis on:
http://gendev.spritesmind.net/forum/viewtopic.php?t=386&postdays=0&postorder=asc&start=167
Based on jt51_phasegen.v, from JT51
*/
module jt12_top (
input rst, // rst should be at least 6 clk&cen cycles long
input clk, // CPU clock
(* direct_enable *) input cen, // optional clock enable, if not needed leave as 1'b1
input [7:0] din,
input [1:0] addr,
input cs_n,
input wr_n,
output [7:0] dout,
output irq_n,
// Configuration
input en_hifi_pcm, // high to enable PCM interpolation on YM2612 mode
// ADPCM pins
output [19:0] adpcma_addr, // real hardware has 10 pins multiplexed through RMPX pin
output [ 3:0] adpcma_bank,
output adpcma_roe_n, // ADPCM-A ROM output enable
input [ 7:0] adpcma_data, // Data from RAM
output [23:0] adpcmb_addr, // real hardware has 12 pins multiplexed through PMPX pin
input [ 7:0] adpcmb_data,
output adpcmb_roe_n, // ADPCM-B ROM output enable
// I/O pins used by YM2203 embedded YM2149 chip
input [7:0] IOA_in,
input [7:0] IOB_in,
output [7:0] IOA_out,
output [7:0] IOB_out,
output IOA_oe,
output IOB_oe,
// Separated output
output [ 7:0] psg_A,
output [ 7:0] psg_B,
output [ 7:0] psg_C,
output signed [15:0] fm_snd_left,
output signed [15:0] fm_snd_right,
output signed [15:0] adpcmA_l,
output signed [15:0] adpcmA_r,
output signed [15:0] adpcmB_l,
output signed [15:0] adpcmB_r,
// combined output
output [ 9:0] psg_snd,
output signed [15:0] snd_right, // FM+PSG
output signed [15:0] snd_left, // FM+PSG
output snd_sample,
input [ 5:0] ch_enable, // ADPCM-A channels
input [ 7:0] debug_bus,
output [ 7:0] debug_view
);
// parameters to select the features for each chip type
// defaults to YM2612
parameter use_lfo=1, use_ssg=0, num_ch=6, use_pcm=1;
parameter use_adpcm=0;
parameter JT49_DIV=2,
YM2203_LUMPED=0;
parameter mask_div=1;
wire flag_A, flag_B, busy;
wire write = !cs_n && !wr_n;
wire clk_en, clk_en_ssg;
// Timers
wire [9:0] value_A;
wire [7:0] value_B;
wire load_A, load_B;
wire enable_irq_A, enable_irq_B;
wire clr_flag_A, clr_flag_B;
wire overflow_A;
wire fast_timers;
wire zero; // Single-clock pulse at the begginig of s1_enters
// LFO
wire [2:0] lfo_freq;
wire lfo_en;
// Operators
wire amsen_IV;
wire [ 2:0] dt1_I;
wire [ 3:0] mul_II;
wire [ 6:0] tl_IV;
wire [ 4:0] keycode_II;
wire [ 4:0] ar_I;
wire [ 4:0] d1r_I;
wire [ 4:0] d2r_I;
wire [ 3:0] rr_I;
wire [ 3:0] sl_I;
wire [ 1:0] ks_II;
// SSG operation
wire ssg_en_I;
wire [2:0] ssg_eg_I;
// envelope operation
wire keyon_I;
wire [9:0] eg_IX;
wire pg_rst_II;
// Channel
wire [10:0] fnum_I;
wire [ 2:0] block_I;
wire [ 1:0] rl;
wire [ 2:0] fb_II;
wire [ 2:0] alg_I;
wire [ 2:0] pms_I;
wire [ 1:0] ams_IV;
// PCM
wire pcm_en, pcm_wr;
wire [ 8:0] pcm;
// Test
wire pg_stop, eg_stop;
wire ch6op;
wire [ 2:0] cur_ch;
wire [ 1:0] cur_op;
// Operator
wire xuse_internal, yuse_internal;
wire xuse_prevprev1, xuse_prev2, yuse_prev1, yuse_prev2;
wire [ 9:0] phase_VIII;
wire s1_enters, s2_enters, s3_enters, s4_enters;
wire rst_int;
// LFO
wire [6:0] lfo_mod;
wire lfo_rst;
// PSG
wire [3:0] psg_addr;
wire [7:0] psg_data, psg_dout;
wire psg_wr_n;
// ADPCM-A
wire [15:0] addr_a;
wire [ 2:0] up_addr, up_lracl;
wire up_start, up_end;
wire [ 7:0] aon_a, lracl;
wire [ 5:0] atl_a; // ADPCM Total Level
wire up_aon;
// APDCM-B
wire acmd_on_b; // Control - Process start, Key On
wire acmd_rep_b; // Control - Repeat
wire acmd_rst_b; // Control - Reset
wire acmd_up_b; // Control - New cmd received
wire [ 1:0] alr_b; // Left / Right
wire [15:0] astart_b; // Start address
wire [15:0] aend_b; // End address
wire [15:0] adeltan_b; // Delta-N
wire [ 7:0] aeg_b; // Envelope Generator Control
wire [ 5:0] adpcma_flags; // ADPMC-A read over flags
wire adpcmb_flag;
wire [ 6:0] flag_ctl;
wire [ 6:0] flag_mask;
wire [ 1:0] div_setting;
wire clk_en_2, clk_en_666, clk_en_111, clk_en_55;
assign debug_view = { 4'd0, flag_B, flag_A, div_setting };
generate
if( use_adpcm==1 ) begin: gen_adpcm
wire rst_n;
jt12_rst u_rst(
.rst ( rst ),
.clk ( clk ),
.rst_n ( rst_n )
);
jt10_adpcm_drvA u_adpcm_a(
.rst_n ( rst_n ),
.clk ( clk ),
.cen ( cen ),
.cen6 ( clk_en_666 ), // clk & cen must be 666 kHz
.cen1 ( clk_en_111 ), // clk & cen must be 111 kHz
.addr ( adpcma_addr ), // real hardware has 10 pins multiplexed through RMPX pin
.bank ( adpcma_bank ),
.roe_n ( adpcma_roe_n ), // ADPCM-A ROM output enable
.datain ( adpcma_data ),
// Control Registers
.atl ( atl_a ), // ADPCM Total Level
.addr_in ( addr_a ),
.lracl_in ( lracl ),
.up_start ( up_start ),
.up_end ( up_end ),
.up_addr ( up_addr ),
.up_lracl ( up_lracl ),
.aon_cmd ( aon_a ), // ADPCM ON equivalent to key on for FM
.up_aon ( up_aon ),
// Flags
.flags ( adpcma_flags ),
.clr_flags ( flag_ctl[5:0] ),
.pcm55_l ( adpcmA_l ),
.pcm55_r ( adpcmA_r ),
.ch_enable ( ch_enable )
);
jt10_adpcm_drvB u_adpcm_b(
.rst_n ( rst_n ),
.clk ( clk ),
.cen ( cen ),
.cen55 ( clk_en_55 ),
// Control
.acmd_on_b ( acmd_on_b ), // Control - Process start, Key On
.acmd_rep_b ( acmd_rep_b ), // Control - Repeat
.acmd_rst_b ( acmd_rst_b ), // Control - Reset
.acmd_up_b ( acmd_up_b ), // Control - New command received
.alr_b ( alr_b ), // Left / Right
.astart_b ( astart_b ), // Start address
.aend_b ( aend_b ), // End address
.adeltan_b ( adeltan_b ), // Delta-N
.aeg_b ( aeg_b ), // Envelope Generator Control
// Flag
.flag ( adpcmb_flag ),
.clr_flag ( flag_ctl[6] ),
// memory
.addr ( adpcmb_addr ),
.data ( adpcmb_data ),
.roe_n ( adpcmb_roe_n ),
.pcm55_l ( adpcmB_l ),
.pcm55_r ( adpcmB_r )
);
assign snd_sample = zero;
jt10_acc u_acc(
.clk ( clk ),
.clk_en ( clk_en ),
.op_result ( op_result_hd ),
.rl ( rl ),
.zero ( zero ),
.s1_enters ( s2_enters ),
.s2_enters ( s1_enters ),
.s3_enters ( s4_enters ),
.s4_enters ( s3_enters ),
.cur_ch ( cur_ch ),
.cur_op ( cur_op ),
.alg ( alg_I ),
.adpcmA_l ( adpcmA_l ),
.adpcmA_r ( adpcmA_r ),
.adpcmB_l ( adpcmB_l ),
.adpcmB_r ( adpcmB_r ),
// combined output
.left ( fm_snd_left ),
.right ( fm_snd_right )
);
end else begin : gen_adpcm_no
assign adpcmA_l = 'd0;
assign adpcmA_r = 'd0;
assign adpcmB_l = 'd0;
assign adpcmB_r = 'd0;
assign adpcma_addr = 'd0;
assign adpcma_bank = 'd0;
assign adpcma_roe_n = 'b1;
assign adpcmb_addr = 'd0;
assign adpcmb_roe_n = 'd1;
assign adpcma_flags = 0;
assign adpcmb_flag = 0;
end
endgenerate
jt12_dout #(.use_ssg(use_ssg),.use_adpcm(use_adpcm)) u_dout(
// .rst_n ( rst_n ),
.clk ( clk ), // CPU clock
.flag_A ( flag_A ),
.flag_B ( flag_B ),
.busy ( busy ),
.adpcma_flags ( adpcma_flags & flag_mask[5:0] ),
.adpcmb_flag ( adpcmb_flag & flag_mask[6] ),
.psg_dout ( psg_dout ),
.addr ( addr ),
.dout ( dout )
);
jt12_mmr #(.use_ssg(use_ssg),.num_ch(num_ch),.use_pcm(use_pcm), .use_adpcm(use_adpcm), .mask_div(mask_div))
u_mmr(
.rst ( rst ),
.clk ( clk ),
.cen ( cen ), // external clock enable
.clk_en ( clk_en ), // internal clock enable
.clk_en_2 ( clk_en_2 ), // input cen divided by 2
.clk_en_ssg ( clk_en_ssg), // internal clock enable
.clk_en_666 ( clk_en_666),
.clk_en_111 ( clk_en_111),
.clk_en_55 ( clk_en_55 ),
.din ( din ),
.write ( write ),
.addr ( addr ),
.busy ( busy ),
.ch6op ( ch6op ),
.cur_ch ( cur_ch ),
.cur_op ( cur_op ),
// LFO
.lfo_freq ( lfo_freq ),
.lfo_en ( lfo_en ),
// Timers
.value_A ( value_A ),
.value_B ( value_B ),
.load_A ( load_A ),
.load_B ( load_B ),
.enable_irq_A ( enable_irq_A ),
.enable_irq_B ( enable_irq_B ),
.clr_flag_A ( clr_flag_A ),
.clr_flag_B ( clr_flag_B ),
.flag_A ( flag_A ),
.overflow_A ( overflow_A ),
.fast_timers( fast_timers ),
// PCM
.pcm ( pcm ),
.pcm_en ( pcm_en ),
.pcm_wr ( pcm_wr ),
// ADPCM-A
.aon_a ( aon_a ), // ON
.atl_a ( atl_a ), // TL
.addr_a ( addr_a ), // address latch
.lracl ( lracl ), // L/R ADPCM Channel Level
.up_start ( up_start ), // write enable start address latch
.up_end ( up_end ), // write enable end address latch
.up_addr ( up_addr ), // write enable end address latch
.up_lracl ( up_lracl ),
.up_aon ( up_aon ),
// ADPCM-B
.acmd_on_b ( acmd_on_b ), // Control - Process start, Key On
.acmd_rep_b ( acmd_rep_b ), // Control - Repeat
.acmd_rst_b ( acmd_rst_b ), // Control - Reset
.acmd_up_b ( acmd_up_b ), // Control - New command received
.alr_b ( alr_b ), // Left / Right
.astart_b ( astart_b ), // Start address
.aend_b ( aend_b ), // End address
.adeltan_b ( adeltan_b ), // Delta-N
.aeg_b ( aeg_b ), // Envelope Generator Control
.flag_ctl ( flag_ctl ),
.flag_mask ( flag_mask ),
// Operator
.xuse_prevprev1 ( xuse_prevprev1 ),
.xuse_internal ( xuse_internal ),
.yuse_internal ( yuse_internal ),
.xuse_prev2 ( xuse_prev2 ),
.yuse_prev1 ( yuse_prev1 ),
.yuse_prev2 ( yuse_prev2 ),
// PG
.fnum_I ( fnum_I ),
.block_I ( block_I ),
.pg_stop ( pg_stop ),
// EG
.rl ( rl ),
.fb_II ( fb_II ),
.alg_I ( alg_I ),
.pms_I ( pms_I ),
.ams_IV ( ams_IV ),
.amsen_IV ( amsen_IV ),
.dt1_I ( dt1_I ),
.mul_II ( mul_II ),
.tl_IV ( tl_IV ),
.ar_I ( ar_I ),
.d1r_I ( d1r_I ),
.d2r_I ( d2r_I ),
.rr_I ( rr_I ),
.sl_I ( sl_I ),
.ks_II ( ks_II ),
.eg_stop ( eg_stop ),
// SSG operation
.ssg_en_I ( ssg_en_I ),
.ssg_eg_I ( ssg_eg_I ),
.keyon_I ( keyon_I ),
// Operator
.zero ( zero ),
.s1_enters ( s1_enters ),
.s2_enters ( s2_enters ),
.s3_enters ( s3_enters ),
.s4_enters ( s4_enters ),
// PSG interace
.psg_addr ( psg_addr ),
.psg_data ( psg_data ),
.psg_wr_n ( psg_wr_n ),
.debug_bus ( debug_bus ),
.div_setting(div_setting)
);
// YM2203 seems to use a fixed cen/3 clock for the timers, regardless
// of the prescaler setting
wire timer_cen = fast_timers ? cen : clk_en;
jt12_timers #(.num_ch(num_ch)) u_timers (
.clk ( clk ),
.clk_en ( timer_cen ),
.rst ( rst ),
.zero ( zero ),
.value_A ( value_A ),
.value_B ( value_B ),
.load_A ( load_A ),
.load_B ( load_B ),
.enable_irq_A( enable_irq_A ),
.enable_irq_B( enable_irq_B ),
.clr_flag_A ( clr_flag_A ),
.clr_flag_B ( clr_flag_B ),
.flag_A ( flag_A ),
.flag_B ( flag_B ),
.overflow_A ( overflow_A ),
.irq_n ( irq_n )
);
// YM2203 does not have LFO
generate
if( use_lfo== 1) begin : gen_lfo
jt12_lfo u_lfo(
.rst ( rst ),
.clk ( clk ),
.clk_en ( clk_en ),
.zero ( zero ),
`ifdef NOLFO
.lfo_rst ( 1'b1 ),
`else
.lfo_rst ( 1'b0 ),
`endif
.lfo_en ( lfo_en ),
.lfo_freq ( lfo_freq ),
.lfo_mod ( lfo_mod )
);
end else begin : gen_nolfo
assign lfo_mod = 7'd0;
end
endgenerate
// YM2203/YM2610 have a PSG
`ifndef NOSSG
generate
if( use_ssg==1 ) begin : gen_ssg
jt49 #(.COMP(3'b01), .CLKDIV(JT49_DIV), .YM2203_LUMPED(YM2203_LUMPED))
u_psg( // note that input ports are not multiplexed
.rst_n ( ~rst ),
.clk ( clk ), // signal on positive edge
.clk_en ( clk_en_ssg), // clock enable on negative edge
.addr ( psg_addr ),
.cs_n ( 1'b0 ),
.wr_n ( psg_wr_n ), // write
.din ( psg_data ),
.sound ( psg_snd ), // combined output
.A ( psg_A ),
.B ( psg_B ),
.C ( psg_C ),
.dout ( psg_dout ),
.sel ( 1'b1 ), // half clock speed
.IOA_out ( IOA_out ),
.IOB_out ( IOB_out ),
.IOA_in ( IOA_in ),
.IOB_in ( IOB_in ),
.IOA_oe ( IOA_oe ),
.IOB_oe ( IOB_oe ),
// Unused:
.sample ( )
);
assign snd_left = fm_snd_left + { 1'b0, psg_snd[9:0],5'd0};
assign snd_right = fm_snd_right + { 1'b0, psg_snd[9:0],5'd0};
end else begin : gen_nossg
assign psg_snd = 10'd0;
assign snd_left = fm_snd_left;
assign snd_right= fm_snd_right;
assign psg_dout = 8'd0;
assign psg_A = 8'd0;
assign psg_B = 8'd0;
assign psg_C = 8'd0;
assign IOA_oe = 0;
assign IOB_oe = 0;
assign IOA_out = 0;
assign IOB_out = 0;
end
endgenerate
`else
assign psg_snd = 10'd0;
assign snd_left = fm_snd_left;
assign snd_right= fm_snd_right;
assign psg_dout = 8'd0;
`endif
wire [ 8:0] op_result;
wire [13:0] op_result_hd;
`ifndef NOFM
jt12_pg #(.num_ch(num_ch)) u_pg(
.rst ( rst ),
.clk ( clk ),
.clk_en ( clk_en ),
// Channel frequency
.fnum_I ( fnum_I ),
.block_I ( block_I ),
// Operator multiplying
.mul_II ( mul_II ),
// Operator detuning
.dt1_I ( dt1_I ), // same as JT51's DT1
// Phase modulation by LFO
.lfo_mod ( lfo_mod ),
.pms_I ( pms_I ),
// phase operation
.pg_rst_II ( pg_rst_II ),
.pg_stop ( pg_stop ),
.keycode_II ( keycode_II ),
.phase_VIII ( phase_VIII )
);
wire [9:0] eg_V;
jt12_eg #(.num_ch(num_ch)) u_eg(
.rst ( rst ),
.clk ( clk ),
.clk_en ( clk_en ),
.zero ( zero ),
.eg_stop ( eg_stop ),
// envelope configuration
.keycode_II ( keycode_II ),
.arate_I ( ar_I ), // attack rate
.rate1_I ( d1r_I ), // decay rate
.rate2_I ( d2r_I ), // sustain rate
.rrate_I ( rr_I ), // release rate
.sl_I ( sl_I ), // sustain level
.ks_II ( ks_II ), // key scale
// SSG operation
.ssg_en_I ( ssg_en_I ),
.ssg_eg_I ( ssg_eg_I ),
// envelope operation
.keyon_I ( keyon_I ),
// envelope number
.lfo_mod ( lfo_mod ),
.tl_IV ( tl_IV ),
.ams_IV ( ams_IV ),
.amsen_IV ( amsen_IV ),
.eg_V ( eg_V ),
.pg_rst_II ( pg_rst_II )
);
jt12_sh #(.width(10),.stages(4)) u_egpad(
.clk ( clk ),
.clk_en ( clk_en ),
.din ( eg_V ),
.drop ( eg_IX )
);
jt12_op #(.num_ch(num_ch)) u_op(
.rst ( rst ),
.clk ( clk ),
.clk_en ( clk_en ),
.pg_phase_VIII ( phase_VIII ),
.eg_atten_IX ( eg_IX ),
.fb_II ( fb_II ),
.test_214 ( 1'b0 ),
.s1_enters ( s1_enters ),
.s2_enters ( s2_enters ),
.s3_enters ( s3_enters ),
.s4_enters ( s4_enters ),
.xuse_prevprev1 ( xuse_prevprev1),
.xuse_internal ( xuse_internal ),
.yuse_internal ( yuse_internal ),
.xuse_prev2 ( xuse_prev2 ),
.yuse_prev1 ( yuse_prev1 ),
.yuse_prev2 ( yuse_prev2 ),
.zero ( zero ),
.op_result ( op_result ),
.full_result ( op_result_hd )
);
`else
assign op_result = 'd0;
assign op_result_hd = 'd0;
`endif
generate
if( use_pcm==1 ) begin: gen_pcm_acc // YM2612 accumulator
assign fm_snd_right[3:0] = 4'd0;
assign fm_snd_left [3:0] = 4'd0;
assign snd_sample = zero;
reg signed [8:0] pcm2;
// interpolate PCM samples with automatic sample rate detection
// this feature is not present in original YM2612
// this improves PCM sample sound greatly
/*
jt12_pcm u_pcm(
.rst ( rst ),
.clk ( clk ),
.clk_en ( clk_en ),
.zero ( zero ),
.pcm ( pcm ),
.pcm_wr ( pcm_wr ),
.pcm_resampled ( pcm2 )
);
*/
wire rst_pcm_n;
jt12_rst u_rst_pcm(
.rst ( rst ),
.clk ( clk ),
.rst_n ( rst_pcm_n )
);
`ifndef NOPCMLINEAR
wire signed [10:0] pcm_full;
always @(*)
pcm2 = en_hifi_pcm ? pcm_full[9:1] : pcm;
jt12_pcm_interpol #(.DW(11), .stepw(5)) u_pcm (
.rst_n ( rst_pcm_n ),
.clk ( clk ),
.cen ( clk_en ),
.cen55 ( clk_en_55 ),
.pcm_wr( pcm_wr ),
.pcmin ( {pcm[8],pcm, 1'b0} ),
.pcmout( pcm_full )
);
`else
assign pcm2 = pcm;
`endif
jt12_acc u_acc(
.rst ( rst ),
.clk ( clk ),
.clk_en ( clk_en ),
.op_result ( op_result ),
.rl ( rl ),
// note that the order changes to deal
// with the operator pipeline delay
.zero ( zero ),
.s1_enters ( s2_enters ),
.s2_enters ( s1_enters ),
.s3_enters ( s4_enters ),
.s4_enters ( s3_enters ),
.ch6op ( ch6op ),
.pcm_en ( pcm_en ), // only enabled for channel 6
.pcm ( pcm2 ),
.alg ( alg_I ),
// combined output
.left ( fm_snd_left [15:4] ),
.right ( fm_snd_right[15:4] )
);
end
if( use_pcm==0 && use_adpcm==0 ) begin : gen_2203_acc // YM2203 accumulator
wire signed [15:0] mono_snd;
assign fm_snd_left = mono_snd;
assign fm_snd_right = mono_snd;
assign snd_sample = zero;
jt03_acc u_acc(
.rst ( rst ),
.clk ( clk ),
.clk_en ( clk_en ),
.op_result ( op_result_hd ),
// note that the order changes to deal
// with the operator pipeline delay
.s1_enters ( s1_enters ),
.s2_enters ( s2_enters ),
.s3_enters ( s3_enters ),
.s4_enters ( s4_enters ),
.alg ( alg_I ),
.zero ( zero ),
// combined output
.snd ( mono_snd )
);
end
endgenerate
`ifdef SIMULATION
integer fsnd;
initial begin
fsnd=$fopen("jt12.raw","wb");
end
always @(posedge zero) begin
$fwrite(fsnd,"%u", {snd_left, snd_right});
end
`endif
endmodule