`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
//
// This file is part of the Next186 Soc PC project
// http://opencores.org/project,next186
//
// Filename: opl3seq.v
// Description: Part of the Next186 SoC PC project, OPL3 Sequencer
// Version 1.0
// Creation date: 13:55:57 02/27/2017
//
// Author: Nicolae Dumitrache 
// e-mail: ndumitrache@opencores.org
//
/////////////////////////////////////////////////////////////////////////////////
// 
// Copyright (C) 2017 Nicolae Dumitrache
// 
// This source file may be used and distributed without 
// restriction provided that this copyright statement is not 
// removed from the file and that any derivative work contains 
// the original copyright notice and the associated disclaimer.
// 
// This source file is free software; you can redistribute it 
// and/or modify it under the terms of the GNU Lesser General 
// Public License as published by the Free Software Foundation;
// either version 2.1 of the License, or (at your option) any 
// later version. 
// 
// This source 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 Lesser General Public License for more 
// details. 
// 
// You should have received a copy of the GNU Lesser General 
// Public License along with this source; if not, download it 
// from http://www.opencores.org/lgpl.shtml 
// 
///////////////////////////////////////////////////////////////////////////////////
// Additional Comments: 
//
// It shares  OPL3 structure (~1.7KB DP RAM) with the main processor. It drives 36 operators at 44100Hz, with at least 45Mhz clock
///////////////////////////////////////////////////////////////////////////////////


`define LFO_AM_TAB_ELEMENTS 210    
`define phase_modulation_o 0     // CH18
`define panA_o             4     // CH18
`define panB_o             8     // CH18
`define lfo_am_depth_o     12    // CH18
`define block_fnum_o			44    // SLOT0
`define chanout_o          46    // SLOT0

`define Cnt_o              4
`define Incr_o					8
`define op1_out_o          12
`define state_o            18
`define AMmask_o           20
`define volume_o           22           
`define FB_o               24
`define mul_o					30
`define sl_o               32      
`define vib_o					34     
`define connect_o          36
`define eg_sh_ar_o         38           
`define eg_sh_dr_o         40           
`define eg_sh_rr_o         42           

module opl3seq(
    input clk,
    input rd, // clk synchronous pulse, read only while <ready> == 1
    output reg [15:0]A,
    output reg [15:0]B,
	output reg ready,
	output reg ram_wr,
	output [11:0]ram_addr,
	input [15:0]ram_rdata,
	output reg [15:0]ram_wdata
  );
   parameter OPL3Struct_base = 12'h000;

   reg [13:0]lfo_am_cnt = 0;
   reg [4:0]r_lfo_am_cnt = 0;
   wire [13:0]lfo_am_cnt1 = lfo_am_cnt + 1'b1;
   reg [9:0]lfo_pm_cnt = 0;
   reg [4:0]LFO_AM; // unsigned 
   reg [3:0]LFO_PM = 0; // unsigned

   reg [5:0]STATE = 0;
   reg [5:0]nextSTATE;
   reg [5:0]RET;
   reg [5:0]ram_offset; // OPL3Struct_base + (CHindex*96 + OPindex*48 + offset)
   reg [4:0]ram_CHindex;  
   reg [4:0]s_ram_CHindex;  
   wire s_ram_CHindex17 = s_ram_CHindex == 17;
   wire s_ram_CHindex18 = s_ram_CHindex == 18;
   wire [4:0]s_ram_CHindex1 = s_ram_CHindex + 1'b1;
   reg ram_OPindex;  
   reg s_ram_OPindex;  
   reg [15:0]ram_rdata_lo;
   reg [15:0]eg_cnt = 0;
//   reg [22:0]noise_rng = 1;
   reg [9:0]env;
   reg [12:0]sinaddr;
   reg [12:0]r_sinaddr;
   wire [12:0]op_calc1;
   wire signed [15:0]op_calc = {{3{op_calc1[12]}}, op_calc1};
   reg [15:0]tmp1;
	wire [9:0]outshift1 = $signed(tmp1[13:0]) >>> ram_rdata[3:0];
   wire [9:0]outshift =  |ram_rdata[3:0] ? outshift1 : 10'b0000000000;
   reg [17:0]pan;
   reg signed [18:0]rAcc;
   wire [15:0]limAcc = rAcc > 32767 ? 16'h7fff : rAcc < -32768 ? 16'h8000 : rAcc[15:0]; //   (~|rAcc[18:15] | &rAcc[18:15]) ? rAcc[15:0] : {rAcc[18], {15{!rAcc[18]}}};
   reg [11:0]phmask;// = (1'b1 << ram_rdata[3:0]) - 1'b1;
   wire phtest = ~|(eg_cnt & phmask);
   reg cond;
   wire [2:0]eg_inc1 = eg_cnt >> ram_rdata[3:0];
   wire [6:0]eg_inc = ram_rdata[14:8] + eg_inc1;
   reg [2:0]r_eg_inc;
   wire [9:0]volume_attack1 = $signed(~ram_rdata[9:0]) >>> (~r_eg_inc[1:0]);
   wire [9:0]volume_attack = {1'b0, ram_rdata[8:0]} + (r_eg_inc[2] ? 10'h000 : volume_attack1);
   reg [9:0]r_volume;
   wire [2:0]exp_r_eg_inc = 1'b1 << r_eg_inc;
   wire [9:0]volume_dsr = ram_rdata[8:0] + exp_r_eg_inc; // new volume for decay/sustain/release
   reg signed [3:0]lfo_fn_table_index_offset;
	reg [11:0]inc_hi;
	reg [15:0]inc_lo;
	reg carry = 1'b0;
   reg cy;
	reg [2:0]phase;
	wire [25:0]mul1 = tmp1[9:0] * ram_rdata_lo;

// tmp1 adder   
	reg [3:0]tmp1op;
   reg [15:0]tmp1op1;
   reg [15:0]tmp1op2;
	
//     .addrb(OPL3Struct_offset + {1'b1, ram_CHindex, ram_OPindex, ram_offset[5:1]})
	assign ram_addr = (ram_OPindex ? (OPL3Struct_base + 6'd24) : OPL3Struct_base) + {ram_CHindex + {ram_CHindex, ram_offset[5]}, ram_offset[4:1]};

   reg [4:0]lfo_am_table[`LFO_AM_TAB_ELEMENTS-1:0];
   initial $readmemh("am.mem", lfo_am_table);
/*    = { 
      0,0,0,0,0,0,0, 1,1,1,1, 2,2,2,2, 3,3,3,3, 4,4,4,4, 5,5,5,5, 6,6,6,6, 7,7,7,7, 8,8,8,8, 9,9,9,9, 10,10,10,10, 11,11,11,11, 12,12,12,12, 13,13,13,13, 14,14,14,14,
      15,15,15,15, 16,16,16,16, 17,17,17,17, 18,18,18,18, 19,19,19,19, 20,20,20,20, 21,21,21,21, 22,22,22,22, 23,23,23,23, 24,24,24,24, 25,25,25,25, 26,26,26, 25,25,25,25,
      24,24,24,24, 23,23,23,23, 22,22,22,22, 21,21,21,21, 20,20,20,20, 19,19,19,19, 18,18,18,18, 17,17,17,17, 16,16,16,16, 15,15,15,15, 14,14,14,14, 13,13,13,13, 12,12,12,12,
      11,11,11,11, 10,10,10,10, 9,9,9,9, 8,8,8,8, 7,7,7,7, 6,6,6,6, 5,5,5,5, 4,4,4,4, 3,3,3,3, 2,2,2,2, 1,1,1,1
   };*/

   reg signed [3:0]lfo_pm_table[127:0];
   initial $readmemh("pm.mem", lfo_pm_table);
/*    = {
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,-1, 0, 0, 0, 1, 0, 0, 0,-1, 0, 0, 0, 2, 1, 0,-1,-2,-1, 0, 1, 1, 0, 0, 0,-1, 0, 0, 0, 3, 1, 0,-1,-3,-1, 0, 1,
      2, 1, 0,-1,-2,-1, 0, 1, 4, 2, 0,-2,-4,-2, 0, 2, 2, 1, 0,-1,-2,-1, 0, 1, 5, 2, 0,-2,-5,-2, 0, 2, 3, 1, 0,-1,-3,-1, 0, 1, 6, 3, 0,-3,-6,-3, 0, 3, 3, 1, 0,-1,-3,-1, 0, 1, 7, 3, 0,-3,-7,-3, 0, 3
   };*/
   
   reg [2:0]eg_inc_tab[119:0];//
   initial $readmemh("eg_inc.mem", eg_inc_tab);
/*    = { 
      4,0, 4,0, 4,0, 4,0, 4,0, 4,0, 0,0, 4,0, 4,0, 0,0, 4,0, 0,0, 4,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,1, 0,0, 0,2, 0,1, 0,1, 0,1, 0,1, 
      0,1, 1,1, 0,1, 1,1, 1,1, 1,1, 1,1, 1,1, 1,1, 1,2, 1,1, 1,2, 1,2, 1,2, 1,2, 1,2, 1,2, 2,2, 1,2, 2,2, 2,2, 2,2, 2,2, 2,2, 3,3, 3,3, 3,3, 3,3, 4,4, 4,4, 4,4, 4,4
   }; // log tablle, 4 = log(0)*/

   sampler sampler_inst
   (
      .clk(clk),
      .index(r_sinaddr),
      .ampl(env[8:0]),
      .sample(op_calc1)
   );

   always @(posedge clk) begin
      STATE <= nextSTATE;
      s_ram_CHindex <= ram_CHindex;
      s_ram_OPindex <= ram_OPindex;
      r_sinaddr <= sinaddr;
      r_lfo_am_cnt <= lfo_am_table[lfo_am_cnt[13:6]]; 
      ram_rdata_lo <= ram_rdata;
      r_eg_inc <= eg_inc_tab[eg_inc];
		lfo_fn_table_index_offset <= lfo_pm_table[{ram_rdata[9:7], LFO_PM}]; //34
      {carry, tmp1} <= tmp1op1 + tmp1op2 + cy;
       pan <= pan >> 1;
      if(pan[0]) rAcc <= rAcc + {{3{ram_rdata[15]}}, ram_rdata};
		      
      case(STATE)
         0: begin
            if(lfo_am_cnt1[13:6] == `LFO_AM_TAB_ELEMENTS) lfo_am_cnt <= 14'h0000;
            else lfo_am_cnt <= lfo_am_cnt1;
            {LFO_PM[2:0], lfo_pm_cnt} <= {LFO_PM[2:0], lfo_pm_cnt} + 1'b1;
            eg_cnt <= eg_cnt + 1'b1;
//            noise_rng <= {noise_rng[0], noise_rng[22:1]} ^ (noise_rng[0] ? 9'h181 : 9'h000);
         end
         1: begin
            LFO_AM <= r_lfo_am_cnt >> {!ram_rdata[0], 1'b0};
            LFO_PM[3] <= ram_rdata[11];
            RET <= 3;
         end
         2: s_ram_CHindex <= s_ram_CHindex17 ? 0 : s_ram_CHindex1;
         3: s_ram_CHindex <= s_ram_CHindex;
         7: RET <= 8;
         8: begin
            s_ram_CHindex <= s_ram_CHindex;
            RET <= 18;
         end
         10: s_ram_OPindex <= 1'b1;
         13: env <= ram_rdata_lo[9:0] + ram_rdata[8:0] + (ram_rdata_lo[15] ? LFO_AM : 0); // env = volume_calc(SLOT1);
         15: {s_ram_CHindex, s_ram_OPindex} <= {s_ram_CHindex, s_ram_OPindex};
         17: {s_ram_CHindex, s_ram_OPindex} <= {s_ram_CHindex, s_ram_OPindex};
         18: s_ram_CHindex <= s_ram_CHindex1;
         20: begin
            pan <= {ram_rdata[1:0], ram_rdata_lo};
            s_ram_CHindex <= 1;
            rAcc <= 0;
         end
         21: s_ram_CHindex <= s_ram_CHindex18 ? s_ram_CHindex : s_ram_CHindex1;
         22: A <= limAcc;
         23: begin
            pan <= {ram_rdata[1:0], ram_rdata_lo};
            s_ram_CHindex <= 1;
            rAcc <= 0;
         end
         24: s_ram_CHindex <= s_ram_CHindex18 ? 0 : s_ram_CHindex1;
         25: B <= limAcc;

 // advance        
         27: cond <= phtest;
         28: r_volume <= tmp1[2] ? volume_attack : volume_dsr;
			29: if(r_volume[9]) r_volume <= (tmp1[2:0] < 3) ? 16'd511 : 16'd0;

 // phase gen        
			32: inc_lo <= ram_rdata;
			33: begin
				inc_hi <= ram_rdata[11:0];
				s_ram_OPindex <= s_ram_OPindex;
			end
			35: cond <= |lfo_fn_table_index_offset;
			36: if(cond && ram_rdata[0]) {inc_hi, inc_lo} <= {mul1, 2'b00} >> ~tmp1[12:10]; 
			37,38: inc_lo <= {4'b0000, inc_hi};
			39: begin
				s_ram_OPindex <= !s_ram_OPindex;
				if(s_ram_OPindex) s_ram_CHindex <= s_ram_CHindex1;
			end
      endcase
   end

   always @(*) begin
      ram_offset = 6'b000000;
      ram_CHindex = s_ram_CHindex;
      ram_OPindex = s_ram_OPindex;
      ram_wdata = 16'h0000;
      ram_wr = 1'b0;
      nextSTATE = STATE + 1'b1;
	  sinaddr = r_sinaddr;
      tmp1op = 4'b1111;   // tmp1 <= tmp1, carry <= 0
      cy = 1'b0;
	  ready = 1'b0;

      case(STATE)
         0: begin
            ram_offset = `lfo_am_depth_o;
            ram_CHindex = 18;
            ram_OPindex = 0;
         end
         1: ram_CHindex = 0;
         2: begin
            ram_offset = `chanout_o;
            ram_wr = 1'b1;
            nextSTATE = s_ram_CHindex17 ? 3 : 2;
         end

// chan_calc         
         3: begin 	// chip.phase_modulation = 0;
            ram_CHindex = 18;
            ram_offset = `phase_modulation_o;
            ram_wr = 1'b1;
         end
         4: ram_offset = `op1_out_o;   // op1_out[0]
         5: ram_offset = `op1_out_o+2; // op1_out[1]
         6: begin
            ram_offset = `FB_o;        // read {FB, wavetable, x, x}
            tmp1op = 4'b0000; // tmp1 <= ram_rdata + ram_rdata_lo; // out = SLOT1->op1_out[0] + SLOT1->op1_out[1];
         end
         7: begin    // SLOT->op1_out[0] = SLOT->op1_out[1];
            ram_offset = `op1_out_o;
            ram_wdata = ram_rdata_lo;
            ram_wr = 1'b1;
            sinaddr = {ram_rdata[10:8], outshift}; // wavetable, out>>FB
            nextSTATE = 11; // call
         end
      
         8: begin
            ram_CHindex = 18;
            ram_offset = `phase_modulation_o;
         end
         9: begin
            ram_offset = `FB_o;       // read {FB, wavetable, x, x}
            ram_OPindex = 1;
         end
         10: begin    // SLOT->op1_out[1] = op_calc
            ram_offset = `op1_out_o+2;
            ram_OPindex = 0;
            ram_wdata = tmp1;
            ram_wr = 1'b1;
            sinaddr = {ram_rdata[10:8], ram_rdata_lo[9:0]}; // wavetable, phase_modulation
         end
         11: ram_offset = `AMmask_o;   // read {TLL, AMmask}
         12: ram_offset = `volume_o;   // read volume[15:0]
         13: ram_offset = `Cnt_o+2;
         14: begin
            ram_offset = `connect_o;
            sinaddr[9:0] = sinaddr[9:0] + ram_rdata[9:0];
            if(env >= 416) nextSTATE = RET;
         end
         15: {ram_CHindex, ram_OPindex, ram_offset} = ram_rdata[11:0];
         16: ram_offset = `connect_o;
         17: begin
            {ram_CHindex, ram_OPindex, ram_offset} = ram_rdata[11:0];
            ram_wdata = ram_rdata_lo + op_calc;//tmp1;
            ram_wr = 1'b1;
            nextSTATE = RET;
            tmp1op = 4'b1110; // tmp1 <= op_calc;
         end
         18: begin
            ram_offset = `panA_o;
            ram_OPindex = 0;
            ram_CHindex = 18;
            nextSTATE = s_ram_CHindex17 ? 19 : 3;
         end
         
         19: ram_offset = `panA_o+2;
         20: begin
            ram_offset = `chanout_o;
            ram_CHindex = 0;
         end
         21: begin
            ram_offset = s_ram_CHindex18 ? `panB_o : `chanout_o;
            nextSTATE = s_ram_CHindex18 ? 22 : 21;
         end
         22: ram_offset = `panB_o+2;
         23: begin
            ram_offset = `chanout_o;
            ram_CHindex = 0;
         end
         24: begin
            ram_offset = `chanout_o;
            nextSTATE = s_ram_CHindex18 ? 25 : 24;
         end

 // envelope        
         25: ram_offset = `state_o;
         26: begin
            case(ram_rdata[2:0])
               3'd1: ram_offset = `eg_sh_rr_o; // release phase
               3'd2: begin // sustain phase
                  ram_offset = `eg_sh_rr_o;
                  if(ram_rdata[8]) nextSTATE = 31; // if (!op->eg_type)
               end
               3'd3: ram_offset = `eg_sh_dr_o; // decay phase
               3'd4: ram_offset = `eg_sh_ar_o; // attack phase
					default: nextSTATE = 31;
            endcase
            tmp1op = 4'b1100; // tmp1 <= ram_rdata; // {eg_type, state}
         end

         27: ram_offset = `volume_o;
         28: ram_offset = `sl_o;       
         29: begin
            ram_offset = `state_o;
				ram_wdata = tmp1;
				case(tmp1[1:0])
					2'b00: if(r_volume[9]) ram_wdata[2:0] = 3'd3;	// attack
					2'b11: if(r_volume >= {1'b0, ram_rdata[8:0]}) ram_wdata[2:0] = 3'd2; // decay
					2'b10:; // sustain
					2'b01: if(r_volume[9]) ram_wdata[2:0] = 3'd0;	// release
				endcase
            ram_wr = cond;
         end
         30: begin
            ram_offset = `volume_o;
				ram_wdata[9:0] = r_volume;
            ram_wr = cond;
         end
			
 // phase gen        
			31: ram_offset = `Incr_o;
			32: ram_offset = `Incr_o+2;

         33: begin
				ram_offset = `block_fnum_o;
				ram_OPindex = 0;
			end
			34: ram_offset = `mul_o;
			35: begin
            ram_offset = `vib_o;
            tmp1op = 4'b0001; //tmp1 <= ram_rdata_lo + lfo_fn_table_index_offset;
         end
			36: ram_offset = `Cnt_o;
			37: begin
            ram_offset = `Cnt_o+2;
            tmp1op = 4'b0100; //{carry, tmp1} <= inc_lo + ram_rdata;
         end
			38: begin
				ram_offset = `Cnt_o;
				ram_wdata = tmp1;
				ram_wr = 1'b1;
            tmp1op = 4'b0100; //{carry, tmp1} <= inc_lo + ram_rdata + carry;
            cy = carry;
			end
			39: begin
				ram_offset = `Cnt_o+2;
				ram_wdata = tmp1;
				ram_wr = 1'b1;
				if(!(s_ram_CHindex17 && s_ram_OPindex)) nextSTATE = 25;
			end
			
         40: begin
				ready = 1'b1;
				nextSTATE = rd ? 0 : 40;
			end
         
      endcase

      case(tmp1op[1:0])
         2'b00: tmp1op1 = ram_rdata;
         2'b01: tmp1op1 = lfo_fn_table_index_offset;
         2'b10: tmp1op1 = op_calc;
         2'b11: tmp1op1 = tmp1;
      endcase
      
      case(tmp1op[3:2])
         2'b00: tmp1op2 = ram_rdata_lo;
         2'b01: tmp1op2 = inc_lo;
         default: tmp1op2 = 16'h0000;
      endcase

      case(ram_rdata[3:0]) // ((1 << ram_rdata[3:0]) - 1)[11:0]
         4'h0: phmask = 12'h000;
         4'h1: phmask = 12'h001;
         4'h2: phmask = 12'h003;
         4'h3: phmask = 12'h007;
         4'h4: phmask = 12'h00f;
         4'h5: phmask = 12'h01f;
         4'h6: phmask = 12'h03f;
         4'h7: phmask = 12'h07f;
         4'h8: phmask = 12'h0ff;
         4'h9: phmask = 12'h1ff;
         4'ha: phmask = 12'h3ff;
         4'hb: phmask = 12'h7ff;
         default: phmask = 12'hfff;
      endcase

   end

endmodule


module sampler(
   input clk, 
   input [12:0]index,
   input [8:0]ampl,
   output [12:0]sample
   );

   reg [12:0]sin_tab[4095:0]; // only the first 4 waveforms are loaded (from 8)
   initial $readmemh("sin_tab.mem", sin_tab);
   
   reg [12:0]exp_tab[6655:0];
   initial $readmemh("exp_tab.mem", exp_tab);

   reg [12:0]logsin;
   wire [13:0]ilog = logsin + {ampl, 4'b0000};
   reg log_rng;
   reg [12:0]isample;
   assign sample = log_rng ? isample : 0;

   always @(posedge clk) begin
      logsin <= sin_tab[index[11:0]];// only the first 4 waveforms are used (from 8)
      log_rng <= ilog < 6656;
      isample <= exp_tab[ilog[12:0]];
   end
   
endmodule

