OpenMPWでSRAMを使う

読む時間が無い場合は例となるリポジトリを用意したので適当に参考にして頑張ってほしい。 https://github.com/Cra2yPierr0t/sky130_sram_test


OpenMPWで自作のメモリを使ったら本当に酷い目にあった。どうやらOpenRAMで生成したメモリを使うと幸せになれるらしいので色々試行錯誤した結果をここに記す。

OpenRAMは任意の構成のSRAMを生成してくれるOSSらしい。 https://openram.org

素敵なOSSであるが、今回はこれを直接使う訳ではない。というか使い方がわからない。ドキュメントどこにあるねん。実はCaravelを用いたSKY130 PDKのインストールと同時に、OpenRAMでビルドされた1kB, 2kBのSRAMも一緒にインスールされているので今回はそれを使おう。

SRAMのVerilogとGDSIIは以下のディレクトリに用意されている。

$PDK_ROOT/sky130B/libs.ref/sky130_sram_macros/

このディレクトリには32x256, 8x1024の1kBのメモリと、32x512の2kBメモリが用意されている。

このメモリを使う順序は以下の通り。

  1. user_project_wrapper.vにメモリを設置
  2. config.tclを編集
  3. macro.cfgで位置を指定
  4. ビルド

以降は32x256の1kBのメモリを例に使い方を説明する。基本的に他のメモリも同じ方法で使うことが出来るが、8x1024の1kBのメモリは留意事項があるため後で補足する。

1. user_project_wrapper.vにメモリを設置

まずは以下のようにuser_project_wrapper.vにメモリを設置する。他のモジュールと同じようにインスタンス名は自由に決めて良いし、複数個インスタンス化出来る。またifdef~endifで囲まれている部分は書いておいた方がよい。

    // write    : when web0 = 0, csb0 = 0
    // read     : when web0 = 1, csb0 = 0, maybe 3 clock delay...?
    // read     : when csb1 = 0, maybe 3 clock delay...?
    sky130_sram_1kbyte_1rw1r_32x256_8 rx_mem(
    `ifdef USE_POWER_PINS
        .vccd1  (vccd1          ),
        .vssd1  (vssd1          ),
    `endif
        // RW
        .clk0   (clk0),   // clock
        .csb0   (csb0),   // active low chip select
        .web0   (web0),   // active low write control
        .wmask0 (wmask0), // write mask (4 bit)
        .addr0  (addr0),  // addr (8 bit)
        .din0   (din0),   // data in (32 bit)
        .dout0  (dout0),  // data out (32 bit)
        // R
        .clk1   (clk1),   // clock
        .csb1   (csb1),   // active low chip select
        .addr1  (addr1),  // addr (8 bit)
        .dout1  (dout1)   // data out (32 bit)
    );

SRAMの各IOの役割については以下の通り。後ろの数字は省略している。

名前 I/O 役割
clk I 1bit クロック入力、立ち下がりで書き込み及び読み出し。
csb I 1bit チップセレクト信号バー。0で選択、1で非選択
web I 1bit 書き込みイネーブルバー。0で書き込み、1で読み出し
wmask I 4bit(SRAMによる) バイトイネーブル。8ビット単位
addr I 8bit(SRAMによる) アドレス
din I 32bit(SRAMによる) データ入力
dout O 32bit(SRAMによる) データ出力

データ書き込み、読み出しの方法は以下の波形の通りであり、読み出しには遅延が存在する。またclkの周期は20ns以上にする必要がある。1

データ読み出しの遅延に関しては、SRAMのコード内に†FIXME: This delay is arbitrary†とか書いてあるので少し待ったほうが良い。

筆者は半導体設計に詳しくないので推測だが、今回使うSRAMのようなメガセルはuser_project_wrapper.vでしかインスタンス化出来ない。

2. config.tclを編集

自分のモジュールとSRAMを接続できたら、次はuser_project_wrapperconfig.tclを編集する。 編集されたconfig.tclの例はここに置いてある。 https://github.com/Cra2yPierr0t/Vthernet-SoC/blob/main/openlane/user_project_wrapper/config.tcl

編集する変数はFP_PDN_MACRO_HOOKSVERILOG_FILES_BLACKBOXEXTRA_LEFSEXTRA_GDS_FILESの4つ。

FP_PDN_MACRO_HOOKS

FP_PDN_MACRO_HOOKSにはインスタンス名 <vdd_net> <gnd_net> <vdd_pin> <gnd_pin>を設定する。基本的にはインスタンス名 vccd1 vssd1 vccd1 vssd1と書けば良い。

set ::env(FP_PDN_MACRO_HOOKS) "\
	rx_mem vccd1 vssd1 vccd1 vssd1"

複数インスタンス化している場合以下のようにコンマ, スペースで区切る。コンマの後ろにスペースを置かないとエラーが発生する。

set ::env(FP_PDN_MACRO_HOOKS) "\
	rx_mem1 vccd1 vssd1 vccd1 vssd1, \
	rx_mem2 vccd1 vssd1 vccd1 vssd1"

VERILOG_FILES_BLACKBOX

VERILOG_FILES_BLACKBOXにはSRAMのVerilogファイルのパスを設定する。これは合成には使われず、シミュレーションを行う場合にそのファイルが参照される。

以下に32x256の1kBのメモリを使う場合の例を載せる。

$::env(PDK_ROOT)/sky130B/libs.ref/sky130_sram_macros/verilog/sky130_sram_1kbyte_1rw1r_32x256_8.v \

EXTRA_LEFS

EXTRA_LEFSには用いるSRAMのLEFファイルのパスを設定する。

以下に32x256の1kBのメモリを使う場合の例を載せる。

$::env(PDK_ROOT)/sky130B/libs.ref/sky130_sram_macros/lef/sky130_sram_1kbyte_1rw1r_8x1024_8.lef \

EXTRA_GDS_FILES

EXTRA_GDS_FILESには用いるSRAMのGDSIIファイルのパスを設定する。

以下に32x256の1kBのメモリを使う場合の例を載せる。

$::env(PDK_ROOT)/sky130B/libs.ref/sky130_sram_macros/gds/sky130_sram_1kbyte_1rw1r_8x1024_8.gds \

DRCエラーの回避

指摘により追加。 config.tclに以下の三行を追加してmagicによるDRCエラーを回避する。

set ::env(MAGIC_DRC_USE_GDS) 0
set ::env(RUN_MAGIC_DRC) 0
set ::env(QUIT_ON_MAGIC_DRC) 0

OpenRAMのSRAMを使う場合magicがどうしてもDRCエラーを発生させるらしい、magicによるDRCは行わなくなるがプリチェックには(殆ど場合)通るので安心してほしい。

これで最低限のconfig.tclの編集が完了した。

3. macro.cfgで位置を指定

次にmacro.ctgでメモリを設置するチップ上の位置を指定する。フォーマットはインスタンス名 X座標 Y座標 Orientationとなっている。 座標の単位はμmであり、メガセルの左下の位置を指定する。Orientationがどの部分のOrientationを示しているのかは知らないが基本的にNにしておけば問題ない。

macro.cfgのパスをMACRO_PLACEMENT_CFGに設定する。これはおそらく既に存在している。

以下に複数個インスタンスがある場合の例を示す。

rx_mem1 100 100 N
rx_mem2 700 100 N

以下が生成されたレイアウトであり、指定した通り(100, 100)にメモリの左下が来ている。

マニュアルにはMACRO_PLACEMENT_CFGは設定しなくても良さげな事が書いてあるが、筆者が試した限りでは上手く行かなかった。

4. ビルド

通常のCaravelにおけるuser_project_wrapperのビルドを実行すれば良い。

make user_project_wrapper

問題なければレイアウト上にメモリが生成されている筈である。

sky130_sram_1kbyte_1rw1r_8x1024_8を使う場合の注意点

執筆段階(mpw-7a)でNUM_MASKSの値を2にしないとエラーが発生する。OpenRAMの中の人が直したと言っていたのでもう必要ないかもしれないが、sky130_sram_1kbyte_1rw1r_8x1024_8を使ってエラーが発生したら試してみてほしい。ただ安全を取るなら32x256を使うべきだと思われる。

一応以下に例を載せる。NUM_MASKSwmask0に変更を加えている。

sky130_sram_1kbyte_1rw1r_8x1024_8 #(.NUM_WMASKS(2)) rx_mem(
    ...
    .web0   (rx_data_vb     ),  // active low write control
    .wmask0 ({wmask0, wmask0}), // write mask (1 bit)
    .addr0  (rx_addr        ),  // addr (10 bit)
    ...

大きなメモリが欲しい場合

僕も欲しい。大人しくOpenRAMを使って生成するか、1kBのSRAMをたくさん置いてアドレスをいい感じにするモジュールを作ればいいと思います。

OpenMPWでメモリをどう作るかは死活問題な気がするのでOpenRAMの使い方を知りたいですね。


  1. ありがとうMatthew Guthausサン