Article #1132

既に発行済みのブログであっても適宜修正・追加することがあります。
We may make changes and additions to blogs already published.

メモリをBSVで書く

posted by sakurai on October 1, 2026 #1132

教科書版の手書きVerilogは、トップの配線、メモリ、クロック生成の3つでした。このうちメモリは、BSVの標準ライブラリBRAMCoreがそのまま当てはまる部分で、手書きのVerilogを残す理由がありません。パターンROM、VRAM、サウンドROMの3つをBSVのモジュールにしました。どれも初期値は今までどおりmemの$readmemhで読み、Vivadoの運用は変わりません。合成はYosysのsynth_xilinx、動作確認はVerilatorとiverilogで、前回までと同じ条件です。

置き場所を決める物差しは、機能が別なら別モジュール、というものです。FSMはROMを読む機能、ROMは内容を持つ機能で、2つは別のモジュールにします。そのうえで、ROMを読むのがそのFSMだけなら、ROMのモジュールはそのFSMの中に置き、FSMがメソッドで呼びます。パターンROMはブリッタの中に、サウンドROMはサウンドFSMの中に置きました。VRAMは2つのクロック域から共有されるので、独立のモジュールとしてトップに置いたままです。

1点目 パターンROMをブリッタの中に置く

図1132.1
図1132.1 パターンROM書き換え前後

パターンROMを読むのはブリッタだけです。それなのにROMはmaster.vにあり、ブリッタはアドレスをレジスタp_addrに置いて外に出し、読み出しデータをワイヤromdataで受けていました。ブリッタのMemPortにはprom_addrとpdinの2ポートがあり、mkGameFSMにも同じ名前のポートが素通しで並んでいました。

書き換え前

// MemPort: 画面メモリ側の面
interface MemPort;
   (* always_ready *) method PAddr_t   prom_addr;
   (* always_ready *) method VAddr_t   vaddr;
   (* always_ready *) method Bool      vwe;
   (* always_ready *) method Pattern_t vdout;
   (* always_enabled *) method Action pdin(Pattern_t d);
   (* always_enabled *) method Action vdin(Pattern_t d);
endinterface
   Reg#(PAddr_t) p_addr <- mkRegU;
   Reg#(VAddr_t) v_addr <- mkRegU;
   Reg#(Bool) fvwe <- mkReg(False);
   Reg#(Bool) fobj  <- mkReg(False),
              fbase <- mkReg(False);
   Reg#(Pattern_t) vd <- mkRegU;
   Wire#(Pattern_t) vdata <- mkWire,
                    romdata <- mkWire;
               action
                  p_addr <= {pack(ys)[7:0], pack(xs)[6:0]};
                  UInt#(8) y_addr = truncate(yd);
                  UInt#(8) x_addr = truncate(xd);
                  v_addr <= {pack(y_addr), pack(x_addr)};
               endaction
               noAction; // リード待ち(1サイクル)
               action
                  case (b.op)
                     BLIT_COPY:  vd <= romdata;
                     BLIT_OR:    vd <= vdata | romdata;
                     BLIT_ANDN:  vd <= vdata & ~romdata;
                     BLIT_ERASE: vd <= 0;
                  endcase
                  xs <= xs + 1;
                  xd <= xd + 1;
               endaction
   interface MemPort mem;
      method PAddr_t   prom_addr = p_addr;
      method VAddr_t   vaddr     = v_addr;
      method Bool      vwe       = fvwe;
      method Pattern_t vdout     = vd;
      method Action pdin(Pattern_t d); romdata <= d; endmethod
      method Action vdin(Pattern_t d); vdata   <= d; endmethod
   endinterface

master.vにはROMのインスタンスと、そこへ渡す2本の配線がありました。

   wire [14:0] prom_addr;
   wire [3:0]  prom_dout;

   mkGameFSM u_game (
      .CLK          (clk),
      .RST_N        (rst_n),
      .COUNT        (COUNT),
      .START_BUTTON (start_db),
      .LEFT_BUTTON  (left_db),
      .RIGHT_BUTTON (right_db),
      .FIRE_BUTTON  (fire_db),
      .tick         (tick),
      .empty        (fifo_empty),
      .fifo_wen     (fifo_wen),
      .SCODE        (scode),
      .prom_addr    (prom_addr),
      .pdin         (prom_dout),
      .vaddr        (vaddr),
      .vdout        (vdout),
      .vwe          (vwe),
      .vdin         (vdin),
      .Gun_Exp      (gun_exp)
   );

   rom #(.WIDTH(4), .AWIDTH(15), .DEPTH(25728), .FILE("prom.mem")) u_prom (
      .clk  (clk),
      .addr (prom_addr),
      .q    (prom_dout)
   );

左矢前のブログ 次のブログ右矢

Leave a Comment

Your email address will not be published.

You may use Markdown syntax. If you include an ad such as http://, it will be invalidated by our AI system.

Please enter the numbers as they are shown in the image above.