/* 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 . 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'b00), .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