FPGA 版 FM 音源 (73) -- FPGA 版 EG (17)

デュアル・ポート RAM を使ったレジスタ・アレイ・インターフェース部の Verilog ソースを下に示します。
"opl3_REG_eg.v"

//
// opl3_REG_eg.v : OPL3 slot parameter register host interface (EG/OP)
//
// 2017/02/12 Created by pcm1723
//

module opl3_REG_eg(
// Input
  reset,   // async. reset
  m_clk,   // master clock
  slot_clk_en, // slot clock enable
  fs_clk_en, // fs clock enable
  wr_trig,   // data write trigger
  wr_addr,   // register address (9-bit)
  wr_data,   // write data    (8-bit)
// Output
  ar,   // AR (4-bit)
  dr,   // DR (4-bit)
  sl,   // SL (4-bit)
  rr,   // RR (4-bit)
  ksl,  // KSL (2-bit)
  tl,   // TL (6-bit)
  am,   // AM
  vib,  // VIB
  egt,  // EGT
  ksr,  // KSR
  mult  // MULT (4-bit)
);
//
  parameter N_SLOT = 6;
//  
  input  reset;
  input  m_clk;
  input  slot_clk_en;
  input  fs_clk_en;
  input  wr_trig;
  input  [8:0] wr_addr;
  input  [7:0] wr_data;
//
  output [3:0] ar;  
  output [3:0] dr;  
  output [3:0] sl;  
  output [3:0] rr; 
  output [1:0] ksl;
  output [5:0] tl;
  output       am;
  output       vib;
  output       egt;
  output       ksr;
  output [3:0] mult;
//
  wire   [31:0] rd_data; // RAM read data
  wire          wr_clk;
  wire          wr_clk_en;
  wire          wr_en;
  wire   [8:0]  wr_addr_remap; // re-mapped write address
  wire          rd_clk;
  wire          rd_clk_en;
  wire          cs;        // "chip select"
//
  reg    [6:0]  rd_addr; // RAM read address
  reg    [1:0]  wr_trig_dly; // delay of wr_trig
//  
// instantiate 2-port RAM
//
  opl3_ram2p8	opl3_ram2p8_inst (
// write only port
    .wrclock  ( wr_clk ),
    .wrclocken( wr_clk_en ),
    .wren     ( wr_en ),
    .wraddress( wr_addr_remap ),
    .data     ( wr_data ),
// read only port
    .rdclock  ( rd_clk ),
    .rdclocken( rd_clk_en ),
    .rdaddress( rd_addr ),
    .q        ( rd_data )
  );
//
  assign wr_clk    = m_clk;
  assign wr_clk_en = wr_en;  
  assign rd_clk    = m_clk;
  assign rd_clk_en = slot_clk_en;
  assign { ar, dr, ksl, tl, am, vib, egt, ksr, mult, sl, rr} = rd_data;
//
// read address generation
  always @(posedge m_clk or posedge reset)
    begin
      if (reset) // async. reset
        begin
          rd_addr <= 7'h00;
        end
      else if (slot_clk_en)
        begin
          if (fs_clk_en)
            begin
              rd_addr <= 7'h00;
            end
          else
            begin
              rd_addr <= ( ((N_SLOT/2-1) == rd_addr[6:0]) ? {~rd_addr[6], 6'h00} : (rd_addr + 7'h01) );
            end // if (fs_clk_en)
        end // if (slot_clk_en)
      else
        begin
          rd_addr <= rd_addr;
        end
    end // always  
//
// write enable control  
  always @(posedge m_clk or posedge reset)  
    begin
      if (reset) // async. reset
        begin
          wr_trig_dly <= 2'b00;
        end
      else
        begin
          wr_trig_dly <= { wr_trig_dly[0], wr_trig };
        end // if
    end // always  
// write enable = positive edge of wr_trig
  assign wr_en = ( cs & ( (~wr_trig_dly[1]) & wr_trig_dly[0] ) );
// "chip select"
  assign cs = ( wr_addr[7] ^ ( wr_addr[6] | wr_addr[5] ) );  
// write address remap
  assign wr_addr_remap = { wr_addr[8], 1'b0, wr_addr[4:0], wr_addr[6:5] };  

endmodule // opl3_REG_eg()  

結果をコンパクトにして見やすくするために、スロット数 N_SLOT = 6 として、アレイ 0 およびアレイ 1 それぞれ 3 個のスロットで構成されるようにしてあります。
また、メガウィザードの実行で生成される 2 ポート RAM 自体の Verilog ファイルについては省略します。
下にシミュレーション結果を示します。 (図をクリックすると拡大します)

opl3_reg_modelsim.png

テストベンチ・プログラム中で、書き込み側のレジスタ・アドレス (wr_addr) は

20h→21h→22h→40h→41h→42h→60h→61h→62h→80h→81h→82h→120h→121h→122h→140h→141h→142h→160h→161h→162h→180h→181h→182h→20h→...

という順番で発生させ、レジスタに書き込むデータの値 (wr_data) は LFSR(Linear Feedback Shift Register) を使った周期 255 の M 系列によって発生させています。
読み出し側のレジスタ・アドレス (rd_addr) は

 00h → 01h → 02h → 40h → 41h → 42h → 00h → ...

という順番で発生させています。