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;