FPGA 版 FM 音源 (42) -- YMF297 (OPN3/OPL3) 測定 (7)

次の 6 つの Verilog-HDL ファイルを示します。 

  • tb_opl3_op.v —— テストベンチ
  • opl3_op.v —— サイン波発生部本体
  • opl3_slb_rom.v —— log2 magnitude サイン波 ROM
  • slbtab_opl3.memh —— 上記 ROM の初期化データ
  • opl3_lb2lin_rom.v —— log2 magnitude — リニア変換 ROM
  • lb2lintab.memh —— 上記 ROM の初期化データ

ModelSim でシミュレーションを行なっただけで、論理合成は試していません。
実際に FPGAインプリメントする場合、ROM 部分の opl3_slb_rom.v と opl3_lb2lin_rom.v を内蔵の組み込み RAM を使用する記述に置き換えることになります。
OPL3 では 1 周期 1024 エントリ (に相当する) サイン波 ROM を使っているので、テストベンチでは 1 クロックで 1 エントリ進むような ph_inc の値に設定しており、10 ビット・カウンタ cnt1024 を使って 1 周期内のサンプル・インデクスをカウントしています。
tb_opl3_op.v

// tb_opl3_op.v : testbench for OPL3 operator sine wave generation part
// 2015/09/24 created by pcm1723

`timescale 1ns/1ns

module tb_opl3_op;
    
// local regs / wires    
    
  reg                reset;   // reset for simulation
  reg                m_clk;   // master clock
  reg                clk_en;  // clock enable
  reg          [9:0] cnt1024; // sample index counter (1/1024)
  reg          [1:0] cnt3;    // wave index counter (1/3)
  reg          [5:0] cnt64;   // WS/FB counter (1/8 * 1/8)
  reg         [18:0] ph_inc;  // phase increment
  
  wire signed [18:0] fb_out;  // feedback output
  wire signed [12:0] op_out;  // operator output
  wire         [2:0] ws;      // Wave Select
  wire         [2:0] fb;      // FeedBack
  
  integer fn;
  
  parameter STEP = 10; // main clock period
  
  initial
    begin
      fn <= $fopen("opl3_op_msim.txt");
      
      reset    = 1; // issue async RESET
      m_clk    = 0;
      clk_en   = 1;
      cnt1024  = 0;
      cnt3     = 0;
      cnt64    = 0;
      ph_inc   = (19'h1 << 9); // period = 1024
           
      #(STEP * 2)
      reset    = 0; // release async RESET
    end // initial
    
// master clock generation 
   
  always #(STEP / 2)
    begin
      m_clk = (~m_clk);
    end // always
    
// dump operator output to a file

  always #STEP 
    begin
      $fdisplay(fn, "%d %d %d %d %d", cnt1024, $signed(op_out), fb, ws, cnt3);    
    end 
    
// sample index counter  
 
  assign cnt1024_tc = &cnt1024;
  
  always @(posedge m_clk)
    begin
      cnt1024 <= (cnt1024_tc ? 10'h0 : (cnt1024 + 10'h1));
    end // always 
 
// wave cycle counter   

  assign cnt3_tc = (cnt1024_tc & cnt3[1] & ~cnt3[0]); // 3 cycles
  
//  assign cnt3_tc = (cnt1024_tc & cnt3[0]);  // 2 cycles
    
  always @(posedge m_clk)
    begin
      if (cnt1024_tc)
        begin
          cnt3 <= (cnt3_tc ? 2'h0 : (cnt3 + 2'h1));
        end
    end

// WS / FB counter
    
  assign cnt64_tc = (cnt3_tc & (&cnt64));
    
  always @(posedge m_clk)
    begin
      if (cnt3_tc)
        begin
          cnt64 <= (cnt64_tc ? 6'h0 : (cnt64 + 2'h1));
        end
    end
    
  assign fb = cnt64[2:0]; // span FB from 0 to 7
  assign ws = 3'h0;
    
//  assign ws = cnt64[2:0]; // span WS from 0 to 7
//  assign fb = 3'h0;
  
// OPL3 operator sine wave genration part instantiation
// (feedback path connected externally)
    
  opl3_op opl3_op1(.reset(reset), 
                   .m_clk(m_clk), 
                   .clk_en(clk_en), 
                   .op_rst(cnt3_tc),
                   .ph_inc(ph_inc), 
                   .mod_in(fb_out), 
                   .eg(12'h0), 
                   .ws(ws), 
                   .fb(fb),
                   .op_out(op_out), 
                   .fb_out(fb_out));    
    
endmodule // tb_opl3_op()

opl3_op.v

// opl3_op.v : OPL3 operator sine wave generation part
// 2015/09/24 created by pcm1723

module opl3_op(reset, m_clk, clk_en, op_rst,
               ph_inc, mod_in, eg, ws, fb,
               op_out, fb_out);

// arguments

  input                reset;   // async reset for simulation
  input                m_clk;   // master clock
  input                clk_en;  // clock enable
  input                op_rst;  // operator reset
  input         [18:0] ph_inc;  // phase acc increment (unsigned 19 bit)
  input  signed [18:0] mod_in;  // modulation input (signed 19 bit)
  input         [11:0] eg;      // EG (12 bit = int 4 bit + frac 8 bit)
  input          [2:0] ws;      // Wave Select (0..7)
  input          [2:0] fb;      // Feedback    (0..7)
  
  output signed [12:0] op_out;  // operator output (signed 13 bit)
  output signed [18:0] fb_out;  // feedback output (signed 19 bit)
  
  reg    signed [12:0] op_out;  // operator output (signed 13 bit)

// local wires / regs
  
  wire          [18:0] ph_sum;  // = (ph_acc + mod_in)
  reg    signed [12:0] out_dly; // 1 sample delayed operator output (signed 13 bit)
  reg           [18:0] ph_acc;  // phase accumlataor (unsigned 19 bit)
  wire           [8:0] ph_int;
  reg           [11:0] slb_tab [0:255]; // lb sine table ROM
  wire           [7:0] ws_dec;  // Wave Select decoder output
  wire           [8:0] slb_a;   // lb sine table address (8 bit (+1 bit for WS=7))
  wire          [11:0] slb_d;   // lb sine table raw data out (12 bit)
  wire          [11:0] slb_ds;  // lb sine table out saturated for WS==7 (12 bit)
  wire          [11:0] slb_eg;  // EG attenuation applied lb sine output
  wire          [11:0] slb_m;   // EG attenuation applied lb sine output with mute
  wire          [10:0] lb2lin_rom_d; // lb to lin ROM data output (11 bit)
  wire   signed [12:0] lin_out; // linear output  
  wire   signed [13:0] lpf_add; // FIR LPF adder (signed 14 bit)
  wire   signed [13:0] lpf_out; // enabled FIR LPF output (signed 14 bit)
  
// WS decoder

  function [7:0] ws_decoder;  
    input [2:0] ws;
    
    case (ws)
      3'h0 : ws_decoder = 8'b00000001;
      3'h1 : ws_decoder = 8'b00000010;
      3'h2 : ws_decoder = 8'b00000100;
      3'h3 : ws_decoder = 8'b00001000;
      3'h4 : ws_decoder = 8'b00010000;
      3'h5 : ws_decoder = 8'b00100000;
      3'h6 : ws_decoder = 8'b01000000;
      3'h7 : ws_decoder = 8'b10000000;
    endcase
  endfunction
  
  assign ws_dec = ws_decoder(ws);
  
  assign ws_0 = ws_dec[0];
  assign ws_1 = ws_dec[1];
  assign ws_2 = ws_dec[2];
  assign ws_3 = ws_dec[3];
  assign ws_4 = ws_dec[4];
  assign ws_5 = ws_dec[5];
  assign ws_6 = ws_dec[6];
  assign ws_7 = ws_dec[7];
  
  assign ws_0_6_7 = (ws_0 | ws_6 | ws_7);
  assign ws_4_5   = (ws_4 | ws_5);
  assign ws_1_4_5 = (ws_1 | ws_4_5);

// phase accumlator

  always @(posedge reset or posedge m_clk)
    begin
      if (reset) // async reset
        ph_acc <= 19'h0; 
      else
        if (clk_en)
          ph_acc <= (op_rst ? 19'h0 : (ph_acc + ph_inc)); 
    end // always
    
  assign ph_sum = (ph_acc + mod_in);  

// phase accum integer part
// 1-bit left shift for WS == 4, WS == 5

  assign ph_int = (ws_4_5 ? {ph_sum[16:9], 1'b0} : ph_sum[17:9]);
  
// ROM address inc / dec direction
  
  assign stab_dir = (ws_7 ? ph_sum[18] : ph_int[8]);
  
// sine ROM address input
  
  assign slb_a = ({9{stab_dir}} ^ ph_int);

// saturate ROM output for WS == 6 (square wave)
  
  assign slb_ds = ({12{~ws_6}} & slb_d);
  
// apply EG attenuation 
// and sine ROM bypass for WS == 7
  
  assign {slb_eg_carr, slb_eg} = (eg + (ws_7 ? {slb_a, 3'h0} : slb_ds));
  
// lb sine value output with mute

  assign mute  = (slb_eg_carr | (ws_1_4_5 & ph_sum[18]) | (ws_3 & ph_sum[17]));
  assign slb_m = {({4{mute}} | slb_eg[11:8]), slb_eg[7:0]};  

// overall SIGN logic

  assign sign = ((ws_0_6_7 & ph_sum[18]) | (ws_4 & ph_sum[17] & ~ph_sum[18]));  

// lb to lin ROM readout with barrel shifter
  
  assign lin_out = {sign, 
                    ({12{sign}} ^ ({lb2lin_rom_d, 1'b0} >> slb_m[11:8]))};

  
// operator output / delay register

  always @(posedge reset or posedge m_clk)
    begin
      if (reset) // async reset
        begin
          op_out  <= 13'h0;
          out_dly <= 13'h0;
        end
      else 
        if (clk_en)
          begin
            op_out  <= (op_rst ? 13'h0 : lin_out);
            out_dly <= (op_rst ? 13'h0 : op_out);
          end
    end

// adder for FIR LPF

  assign lpf_add = ({op_out[12], op_out} + {out_dly[12], out_dly});      
  assign fb_en   = ( |fb );
  assign lpf_out = ({14{fb_en}} & lpf_add);
  
  assign fb_out = ({ {5{lpf_out[13]}}, lpf_out } << fb);  
 
// lb (log2 magnitude) sine ROM instantiation                         
  
  opl3_slb_rom opl3_slb_rom(.reset(reset),
                            .m_clk(m_clk),
                            .clk_en(1'b1), 
                            .ain(slb_a[7:0]),
                            .dout(slb_d));
                           
// lb (log2 magnitude) to linear conversion ROM instantiation                            

  opl3_lb2lin_rom opl3_lb2lin_rom(.reset(reset),
                                  .m_clk(m_clk),
                                  .clk_en(1'b1), 
                                  .ain(slb_m[7:0]),
                                  .dout(lb2lin_rom_d));
              
  endmodule // opl3_op()

opl3_slb_rom.v

// opl3_slb_rom.v : OPL3 lb (log2 magnitude) sine table
// 2015/09/25 created by pcm1723

module opl3_slb_rom(reset, m_clk, clk_en,
                    ain, dout);

// arguments

  input         reset;   // async reset for simulation
  input         m_clk;   // master clock
  input         clk_en;  // clock enable
  input   [7:0] ain;     // address input (8 bit)
               
  output [11:0] dout;    // data output (12 bit)
 
// local regs / wires

  reg    [11:0] slb_tab [0:255]; // lb sine table ROM
 
// ROM function

  assign dout = slb_tab[ain];
  
// lb sine ROM data initialize

  initial
    begin
      $readmemh("slbtab_opl3.memh", slb_tab, 8'h00, 8'hff);
    end // initial
    
  endmodule // opl3_slb_rom()

opl3_lb2lin_rom.v

// opl3_lb2lin_rom.v : 8-bit lb fraction to 12-bit linear conversion ROM
// 2015/09/25 created by pcm1723

module opl3_lb2lin_rom(reset, m_clk, clk_en, 
                       ain, dout);                  
                   
 // arguments
 
  input         reset;  // reset for simulation
  input         m_clk;  // master clock
  input         clk_en; // clock enable
  input   [7:0] ain;    // ROM address input (8 bit)
  
  output [10:0] dout;   // ROM data output (11 bit)

// local regs / wires

  reg           [10:0] lb2lin [0:255];   // lb to lin ROM
  
// ROM readout
  
  assign dout = lb2lin[ain];

// ROM data initialize

  initial
    begin
      $readmemh("lb2lintab.memh", lb2lin, 8'h00, 8'hff);
    end // initial
endmodule // opl3_lb2lin_rom()

slbtab_opl3.memh

//--
//--  file: "slbtab_opl3.memh"
//--  1024 point sine table
//--  only first 256 points (0..pi/2) for symmetry
//--

//depth=256;
//width=12;
//address_radix=hex;
//data_radix=hex;
//content
//begin
//--   +0  +1  +2  +3  +4  +5  +6  +7
//--  --- --- --- --- --- --- --- ---
@000  859 6c3 607 58b 52e 4e4 4a6 471 
@008  443 41a 3f5 3d3 3b5 398 37e 365 
@010  34e 339 324 311 2ff 2ed 2dc 2cd 
@018  2bd 2af 2a0 293 286 279 26d 261 
@020  256 24b 240 236 22c 222 218 20f 
@028  206 1fd 1f5 1ec 1e4 1dc 1d4 1cd 
@030  1c5 1be 1b7 1b0 1a9 1a2 19b 195 
@038  18f 188 182 17c 177 171 16b 166 
@040  160 15b 155 150 14b 146 141 13c 
@048  137 133 12e 129 125 121 11c 118 
@050  114 10f 10b 107 103 0ff 0fb 0f8 
@058  0f4 0f0 0ec 0e9 0e5 0e2 0de 0db 
@060  0d7 0d4 0d1 0cd 0ca 0c7 0c4 0c1 
@068  0be 0bb 0b8 0b5 0b2 0af 0ac 0a9 
@070  0a7 0a4 0a1 09f 09c 099 097 094 
@078  092 08f 08d 08a 088 086 083 081 
@080  07f 07d 07a 078 076 074 072 070 
@088  06e 06c 06a 068 066 064 062 060 
@090  05e 05c 05b 059 057 055 053 052 
@098  050 04e 04d 04b 04a 048 046 045 
@0a0  043 042 040 03f 03e 03c 03b 039 
@0a8  038 037 035 034 033 031 030 02f 
@0b0  02e 02d 02b 02a 029 028 027 026 
@0b8  025 024 023 022 021 020 01f 01e 
@0c0  01d 01c 01b 01a 019 018 017 017 
@0c8  016 015 014 014 013 012 011 011 
@0d0  010 00f 00f 00e 00d 00d 00c 00c 
@0d8  00b 00a 00a 009 009 008 008 007 
@0e0  007 007 006 006 005 005 005 004 
@0e8  004 004 003 003 003 002 002 002 
@0f0  002 001 001 001 001 001 001 001 
@0f8  000 000 000 000 000 000 000 000 
//end;

lb2lintab.memh

//--
//--  file: "lb2lintab.memh"
//--  table for 8-bit lb fraction to 11-bit linear conversion
//--  (256 entries)
//--

//depth=256;
//width=11;
//address_radix=hex;
//data_radix=hex;
//content
//begin
//--   +0  +1  +2  +3  +4  +5  +6  +7
//--  --- --- --- --- --- --- --- ---
@000  7fa 7f5 7ef 7ea 7e4 7df 7da 7d4 
@008  7cf 7c9 7c4 7bf 7b9 7b4 7ae 7a9 
@010  7a4 79f 799 794 78f 78a 784 77f 
@018  77a 775 770 76a 765 760 75b 756 
@020  751 74c 747 742 73d 738 733 72e 
@028  729 724 71f 71a 715 710 70b 706 
@030  702 6fd 6f8 6f3 6ee 6e9 6e5 6e0 
@038  6db 6d6 6d2 6cd 6c8 6c4 6bf 6ba 
@040  6b5 6b1 6ac 6a8 6a3 69e 69a 695 
@048  691 68c 688 683 67f 67a 676 671 
@050  66d 668 664 65f 65b 657 652 64e 
@058  649 645 641 63c 638 634 630 62b 
@060  627 623 61e 61a 616 612 60e 609 
@068  605 601 5fd 5f9 5f5 5f0 5ec 5e8 
@070  5e4 5e0 5dc 5d8 5d4 5d0 5cc 5c8 
@078  5c4 5c0 5bc 5b8 5b4 5b0 5ac 5a8 
@080  5a4 5a0 59c 599 595 591 58d 589 
@088  585 581 57e 57a 576 572 56f 56b 
@090  567 563 560 55c 558 554 551 54d 
@098  549 546 542 53e 53b 537 534 530 
@0a0  52c 529 525 522 51e 51b 517 514 
@0a8  510 50c 509 506 502 4ff 4fb 4f8 
@0b0  4f4 4f1 4ed 4ea 4e7 4e3 4e0 4dc 
@0b8  4d9 4d6 4d2 4cf 4cc 4c8 4c5 4c2 
@0c0  4be 4bb 4b8 4b5 4b1 4ae 4ab 4a8 
@0c8  4a4 4a1 49e 49b 498 494 491 48e 
@0d0  48b 488 485 482 47e 47b 478 475 
@0d8  472 46f 46c 469 466 463 460 45d 
@0e0  45a 457 454 451 44e 44b 448 445 
@0e8  442 43f 43c 439 436 433 430 42d 
@0f0  42a 428 425 422 41f 41c 419 416 
@0f8  414 411 40e 40b 408 406 403 400 
//end;