Article #1133

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

メモリをBSVで書く (2)

posted by sakurai on October 2, 2026 #1133

書き換え後は、Blitter.bsvにmkPromというROMのモジュールを立て、ブリッタはそれを置いてreqとdataを呼ぶだけにします。mkPromの実体はBRAMCoreのmkBRAMCore1Loadで、引数は語数、出力レジスタの有無、ファイル名、ファイルが2進かどうかです。

import StmtFSM::*;
import FIFOF::*;
import BRAMCore::*;
// MemPort: 画面メモリ側の面。PROM は内蔵なので VRAM のポート A だけが出る
interface MemPort;
   (* always_ready *) method VAddr_t   vaddr;
   (* always_ready *) method Bool      vwe;
   (* always_ready *) method Pattern_t vdout;
   (* always_enabled *) method Action vdin(Pattern_t d);
endinterface
// ================= パターン ROM =================
// 25728 x 4 の同期 ROM。prom.mem を $readmemh で読む。req したアドレスの内容が
// 次の周期から data に出る。実体は BRAMCore ライブラリの mkBRAMCore1Load で、
// 生成 Verilog では bsc 付属の BRAM1Load.v がインスタンスされる。
// FSM とは機能が別なので、ブリッタの中に置く別モジュールにしてある。
// 語数は PROM_DEPTH で作り分けられる(テストベンチ用)。
`ifdef PROM_DEPTH
   Integer promDepth = `PROM_DEPTH;
`else
   Integer promDepth = 25728;
`endif

interface Prom_ifc;
   (* always_ready *) method Action    req(PAddr_t addr);
   (* always_ready *) method Pattern_t data;
endinterface

(* synthesize *)
module mkProm(Prom_ifc);
   BRAM_PORT#(PAddr_t, Pattern_t) rom <- mkBRAMCore1Load(promDepth, False, "prom.mem", False);
   method Action    req(PAddr_t addr) = rom.put(False, addr, ?);
   method Pattern_t data = rom.read;
endmodule
   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;

   // パターン ROM。機能は mkProm という別のモジュールで、ブリッタはそれを置いて
   // req と data を呼ぶだけ。階層図では mkBlitter の中に mkProm の箱として見える。
   Prom_ifc prom <- mkProm;
               action
                  prom.req({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 <= prom.data;
                     BLIT_OR:    vd <= vdata | prom.data;
                     BLIT_ANDN:  vd <= vdata & ~prom.data;
                     BLIT_ERASE: vd <= 0;
                  endcase
                  xs <= xs + 1;
                  xd <= xd + 1;
               endaction
   interface MemPort mem;
      method VAddr_t   vaddr     = v_addr;
      method Bool      vwe       = fvwe;
      method Pattern_t vdout     = vd;
      method Action vdin(Pattern_t d); vdata   <= d; endmethod
   endinterface

アドレスを置くactionはレジスタへの代入からprom.reqに、ラッチのactionはワイヤからprom.dataに変わりました。reqしたアドレスの内容は次の周期からdataに出るので、間のnoActionを含めて1画素あたりのサイクル数は変わりません。消えたのはp_addrレジスタ、romdataワイヤ、MemPortの2ポート、mkGameFSMの同名の2ポート、master.vのROMインスタンスと2本の配線です。mkPromは(* synthesize *)付きなので生成物にmkProm.vが1つ増え、Vivadoの階層図ではmkBlitterの中にmkPromの箱が出ます。ブリッタのFSMとROMが別のモジュールであることが、字面でも階層図でも見えます。

生成されたmkProm.vの中では、bsc付属のBRAM1Load.vがFILENAMEにprom.mem、MEMSIZEに25728を与えてインスタンスされます。BRAM1Load.vの中が$readmemhなので、memの運用は今までと同じです。語数はpromDepthにあり、テストベンチのように別の語数で作りたいときは-D PROM_DEPTH=32768のように与えます。


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

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.