RP2040 PIO 覚え書き
RP2040 データシートを読んだまとめ
https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf
元の文書は改変配布禁止ですので翻訳はできません。ただの覚え書きです。
レジスタタイプについての説明は Appendix A: Register Field Types にある。
目次
- PIO ブロックの概要
- レジスタ
- FIFO
- 自動プッシュと自動プル
- ピン割り当て
- サイドセット
- IRQ
- 入力同期
- ラッピング
- ストール
- 指令、ディレクティブ
- 値
- 式
- コメント
- ラベル
- 命令、インストラクション
- 疑似命令
- nop
- 動作クロック
- 命令実行
- 初期化
- 節約方法
- インラインアウト
PIO ブロックの概要
RP2040 の PIO は2つのブロックがあり、それぞれ4つずつのステートマシンがある。
このステートマシンがそれぞれ個別に利用でき同時に8つのステートマシンが動作できる。
- 動作周波数はシステムクロックを分割したもので、最大周波数はシステムクロックと同じ
- GPIO のピンを PIO から使う場合、利用するピンに関して設定しておく
- コアとのデータやり取りは TX/RX FIFO を介して行う
- コアから見て TX が送信、RX が受信(ステートマシン側から見ると逆)
命令用のメモリは PIO ブロックごとに 32 命令分ある。これを4つのステートマシンで共有する。
そのため、各ステートマシンで走らせるコードはできるだけ小さくしたい。
レジスタ
すべて 32-bit。
FIFO
- FIFO はステートマシンごとに TX 4 本、RX 4 本
- 方向を設定すると TX 8 本または RX 8 本にできる
- SHIFTCTRL_FJOIN で一方通行にして 8 本利用できる
- 通信の TX と RX を別々のステートマシンで実装する場合などに有効
- 4:4, 0:8, 8:0 のどれかの組み合わせのみ可
- 0 本の側で PULL や PUSH すると stall する
自動プッシュと自動プル
有効にしておくと IN や OUT 命令で入力または出力でシフトしたビット数のカウントがしきい値に達すると、自動的に次のデータを対応した PUSH または PULL してくれる。
SHIFTCTRL_PULL_THRESHやSHIFTCTRL_PUSH_THRESHレジスタでしきい値を設定する。
SHIFTCTRL_AUTOPULLやSHIFTCTRL_AUTOPUSHレジスタで有効にする。
ピン割り当て
IN, OUT, SET, サイドセットの4つの GPIO ピンの操作はそれぞれのベースピンレジスタ設定を基準にしてピンが決まる。
ベースピン 7、ピン指定 3 のとき、実際のピンは PIN 10 となる
WAIT GPIO のみベースが適用されない。
- OUT 命令は最大 32 ビットまで書き込める。Destination にしたがって PINS または PINDIRS に適用される。
- 最下位ビットが PINCTRL_OUT_BASE から PINCTRL_OUT_COUNT だけのピンにマッピングされ、GPIO31 を越えるとラップされる。
- SET 命令は最大 5 ビットまで書き込める。Destination にしたがって PINS または PINDIRS に適用される。
- 最下位ビットが PINCTRL_SET_BASE から PINCTRL_SET_COUNT だけのピンにマッピングされ、GPIO31 を越えるとラップされる。
- サイドセットは最大 5 ビットまで書き込める。EXECCTRL_SIDE_PINDIR にしたがって PINS または PINDIRS に適用される。
- 最下位ビットが PINCTRL_SIDESET_BASE から PINCTRL_SIDESET_COUNT だけのピンにマッピングされる。EXECCTRL_SIDE_ENが設定されているときは - 1。
サイドセットが OUT/SET 命令と同じサイクルで重複した場合、重なった範囲でサイドセットが優先される。
サイドセット
命令実行と同時にピンのレベルまたは向きを操作する。命令を節約、2つ以上のピン操作が速くなる
- サイドセットはディレイと指定フィールドを共有しており、どちらかに使用するビット数をPINCTRL_SIDESET_COUNTで指定する
- 一度に最大5ピンまで操作できるが、その時はディレイが使えない
- .side_set 1 opt などと opt がつくとサイドセットが含まれていない命令がある
- このとき、サイドセットの有無の判定に MSB 1ビットがフラグとして使われる
- .side_set 1 opt なら 2 ビットがサイドセットに使われ、残りの 3 ビットがディレイ
- ディレイを 7 サイクルまで使用したければ 3 ビットをディレイに、サイドセットは最大で 2 ピン。
- 命令1つ+7サイクルディレイで8サイクル消費など
- サイドセットはSIDESET_COUNTで指定されたビット数だけ MSB 側を使用する
- 命令がストールしてもサイドセットはすぐに有効
- SIDESET_BASE で指定されたピンが最初のサイドセットピン
- サイドセットに使っているビットのうち LSB が SIDESET_BASE に相当する
入力同期
ピンには 2 フリップフロップ同期回路がある。ピンの入力を安定にするためのものだが、このせいで入力サンプリングは 2 サイクル遅れる。
IN 命令の実行が遅れるわけではなく、ピンへの入力のレベルが変わったとき、サンプリングされて確定するのが 2 サイクル遅れる。
ピンごとにこの同期回路をバイパスでき、INPUT_SYNC_BYPASS レジスタで設定する。
ラッピング
.wrap_target ... .wrap の間で自動的にゼロサイクルジャンプしてくれる機能。
- ラッピングの命令数はゼロのため、命令メモリを節約できる
- サイクル数はゼロ
- レジスタで繰り返し範囲のアドレス設定を行う
- ステートマシンごとに1つだけ使用できる
ストール
ストールすると停止状態になる。ストールする条件は以下のものがある。
- WAIT 命令の条件が満たされていない時、ピンの変化待ちなど
- ブロックありの PULL で TX FIFO が空のとき、ブロックありの PUSH で RX FIFO が一杯のとき
- IRQ WAIT 命令でフラグがセットされているときのクリア待ち
- 自動プルが有効の時の OUT 命令、OSR がしきい値に達していて TX FIFO が空の時
- 自動プッシュが有効の時の IN 命令、ISR がしきい値に達していて RX FIFO が一杯のとき
プログラムカウンタは進まない。ディレイが指定されている時はストールがクリアされるまでディレイも消費されない。
指令、ディレクティブ
.define ( PUBLIC ) <symbol> <value>
整数シンボル名 <symbol> を定義して値 <value> を割り当てる。.define が最初のプログラムより前に記載されるとすべてのプログラムに対してグローバルとなる。それ以外ではプログラムローカルとなる。PUBLIC が指定されている場合、シンボルが出力に含まれる。
.program <name>
新しいプログラム <name> を開始する。名前はコード中で使用されるため、アルファベットまたは数字、アンダースコアかつ数字で始まらないこと。プログラムは次の .program が出現するまでの範囲。PIO 命令はこの中でのみ有効。
.origin <offset>
プログラムを読み込む PIO 命令メモリのオフセットを指定するオプション指令。
プログラム外では無効。
.side_set <count> (opt) (pindirs)
<count> はサイドセットのビット数を指定する。opt が指定された場合、side <value>が命令に対してオプションとなる(追加で1ビットがフラグとして必要)。pindirs を指定すると値はピンの向きの変更に適用される。プログラム中の最初の命令の前で有効。
.wrap_target
プログラムごとに最大一つ。.wrap との間を繰り返すために使う。プログラム外では無効。
.wrap
プログラムごとに最大一つ。.wrap_target との間を繰り返すために使う。プログラム外では無効。
.lang_opt <lang> <name> <option>
特定の言語用ジェネレータのためのオプション。プログラム外では無効。
.word <value>
プログラムに16ビット直値を入れる。プログラム外では無効。
値
(-)0-9 (10進数、符号付き), 0x1 (16進数), 0b1 (2進数)
.define されたシンボル
ラベル
(式) (括弧が必要)
式
式 + 式、式 - 式、式 * 式、式 / 式、- 式、:: 式 (ビット逆転)、値
コメント
// または ; から行末まで。
/* */ の C 言語のコメントも可能。
ラベル
( PUBLIC ) <value>:
PUBLIC はオプション。ラベルは自動的に .define に置き換えられる。
命令、インストラクション
<instruction> ( side <side_set_value ) ([<delay_value>]) <instruction>: 命令 <side_set_value>: 命令の開始時にサイドセットピンに値を適用する。 .side_set が指定されていないと無効。サイドセットがオプションの場合は指定が無くてもよいが、 オプションでない場合は必要。side_set_value は .side_set で指定したビット数に合っていること。 <delay_value>: 命令の終了後にディレイを追加する。ディレイは 0-31 (5-bit) だが .side_set によりビット数が減る。delay_value が指定されないとディレイは無し。
命令、キーワード、指令は大文字と小文字を区別しない。
命令の記載説明のコンマはオプション。命令の記載時にコンマは無くても良い。
すべての PIO 命令は1クロックサイクルで実行される。
命令の挙動を PIO レジスタで切り替えることが多々あるので注意が必要。
Bit | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
JMP | 0 | 0 | 0 | Delay/side-set | Condition | Address | ||||||||||
WAIT | 0 | 0 | 1 | Delay/side-set | Pol | Source | Index | |||||||||
IN | 0 | 1 | 0 | Delay/side-set | Source | Bit count | ||||||||||
OUT | 0 | 1 | 1 | Delay/side-set | Destination | Bit count | ||||||||||
PUSH | 1 | 0 | 0 | Delay/side-set | 0 | IfF | Blk | 0 | 0 | 0 | 0 | 0 | ||||
PULL | 1 | 0 | 0 | Delay/side-set | 1 | IfE | Blk | 0 | 0 | 0 | 0 | 0 | ||||
MOV | 1 | 0 | 1 | Delay/side-set | Destination | Op | Source | |||||||||
IRQ | 1 | 1 | 0 | Delay/side-set | 0 | Clr | Wait | Index | ||||||||
SET | 1 | 1 | 1 | Delay/side-set | Destination | Data |
PUSH と PULL 命令の違いは 7 ビット目。
ディレイとサイドセットはフィールドを共有しているが、使用するビット数をレジスタで変更できる。
JMP
Bit | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
JMP | 0 | 0 | 0 | Delay/side-set | Condition | Address |
- Condition
- 000: 条件なしでいつもジャンプ
- 001: !X: X がゼロ(X否定)
- 010: X--: X がゼロでない、後置デクリメント(分岐と関係なくデクリメントされる)
- 011: !Y: Y がゼロ(Y否定)
- 100: Y--: Y がゼロでない、後置デクリメント(分岐と関係なくデクリメントされる)
- 101: X!=Y: XとYは一致しない
- 110: PIN: PIN の入力が 1 でジャンプ、ピンは JMP_PIN レジスタで指定
- 111: !OSRE: OSRE が空でない
- Address: ジャンプ先の命令アドレス。PIO 命令メモリ中の絶対アドレス指定。
Condition が真のとき、プログラムカウンタを Address にする。真でないときは何もしない。
Condition に関係なくディレイは実行される。条件判定されてプログラムカウンタが更新された後でディレイが実行される。
JMP PIN は JMP_PIN レジスタで選択した GPIO で判定される。
デクリメントしない判定方法は無い。ピンの入力がゼロの判定も無い。
後置デクリメント x-- または y-- でループさせる場合、レジスタの数値 + 1 回ループする。
つまり do {} while (x--); となる。
jmp ( <cond> ) <target> <cond>: Condition, オプション <target>: ラベルまたは、最初の命令の位置をゼロとしてそこからのオフセット値
WAIT
Bit | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
WAIT | 0 | 0 | 1 | Delay/side-set | Pol | Source | Index |
- Polarity:
- 1: 1 を待つ
- 0: 0 を待つ
- Source: 待つ値
- Index: 確認するピンまたはビット指定
WAIT 1 IRQ は割り込みコントローラにフラグがあり、システムの IRQ ハンドラと競合することがあるため注意。
wait <polarity> gpio <gpio_num> wait <polarity> pin <pin_num> wait <plarity> irq <irq_num> ( rel ) <polarity>: Polarity 指定、0 または 1 <pin_num>: 入力ピン番号、入力マッピングで決まる <gpio_num>: 実際の GPIO ピン番号 <irq_num> ( rel ): 待つ irq 番号で 0-7。rel が指定されると実際の irq 番号(irq_num10)は 下位2ビットがステートマシンの irq 番号 sm_num10 との合計(irq_num10 + sm_num10)に置き換えられる。
IN
Bit | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
IN | 0 | 1 | 0 | Delay/side-set | Source | Bit count |
- Source:
- 000: PINS
- 001: X
- 010: Y
- 011: NULL: 不要なビットをシフトで捨てる時など
- 100: Reserved
- 101: Reserved
- 110: ISR
- 111: OSR
- Bit count: ISR にシフト入力するビット数。1-32ビット、32は00000として表現される
Source から Bit count だけシフトして ISR に入力される。シフト方向はステートマシンごとに SHIFTCTRL_IN_SHIFTDIR で指定する。入力シフトカウントが Bit count だけ増加する、最大 32。
自動プッシュが有効のとき、しきい値(SHIFTCTRL_PUSH_THRESH)に達していると ISR のデータが RX FIFO へ送られる。
自動プッシュは実行サイクル数への影響なし。
自動プッシュが発生したときに RX FIFO が一杯なら stall する。
自動プッシュが行われると ISR の内容はすべてのビットがゼロになり、入力シフトカウントはクリアされる。
IN 命令ではソースデータの Bit count の最下位が使用される。Bit count + PINCTRL_IN_BASE から入力される。
まず、ISR が設定に従って左または右にシフトされ、その空きにデータビットが入力される。ビットオーダーはシフト方向に影響されない。
in <source>, <bit_count> <source>: 上記の Source <bit_count>: シフトするビット数 (1-32)
OUT
Bit | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
OUT | 0 | 1 | 1 | Delay/side-set | Destination | Bit count |
- Destination
- 000: PINS:
- 001: X
- 010: Y
- 011: NULL: データを捨てる時
- 100: PINDIRS
- 101: PC
- 110: ISR (さらに、ISR シフトカウンタを Bit count にする)
- 111: EXEC (OSR シフトデータを命令として実行)
- Bit count: OSR からシフト出力するビット数。1-32ビット、32 は00000として表現
Bit count だけ OSR からシフトしてそのビットを Destination に書き込む。出力シフトカウンタが Bit count だけ増加する、上限は 32。
OSR から Bit count で指定されたビットが Destination に書き込まれ、残りのビットはすべてゼロ。
SHIFTCTRL_OUT_SHIFTDIRが右のとき下位ビット、左のときは上位ビット。
PINS および PINDIRS は OUT ピンマッピングを使用する。
自動プルが有効の時、SHIFTCTRL_PULL_THRESH に達していると TX FIFO から OSR へ自動的に読み込まれる。出力シフトカウントはゼロにクリアされる。TX FIFO が空のときは stall する。
自動プルが発生しても 1 サイクルで実行される。
OUT EXEC ではまず OUT が実行され、つぎのサイクルで OSR からの命令が実行される。最初の OUT 実行ではディレイサイクルが無視されるが、実行される命令では通常通りに実行される。
OUT PC では OSR から取り出したアドレスへ無条件にジャンプする。
out <destination>, <bit_count> <destination>: 上記の Destination <bit_count>: シフトするビット数 (1-32)
PUSH
Bit | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
PUSH | 1 | 0 | 0 | Delay/side-set | 0 | IfF | Blk | 0 | 0 | 0 | 0 | 0 |
ISR のデータを RX FIFO に 32-bit ワードとして書き込む。書き込み後に ISR はゼロクリアされる。
- IfFull: 1 のとき、入力シフトカウントがしきい値 SHIFTCTRL_PUSH_THRESH (自動プッシュと同じレジスタ)に達していなければ何もしない。
- Block: 1 のとき、RX FIFO が一杯ならストールする。
PIO アセンブラはデフォルトで Block ビットを設定する。Block ビットが設定されていなければ PUSH は RX FIFO が一杯でもストールせず、すぐに次の命令に進む。このとき、FIFO の状態や内容は変化しない。しかし、ISR はゼロクリアされ、FDEBUG_RXSTALL フラグが設定される (RX FIFO が一杯の時のブロッキング PUSH や自動プッシュと同じ挙動)。
push ( iffull ) push ( iffull ) block push ( iffull ) noblock iffull: 上記の IfFull == 1 と等価。指定されていないとき、デフォルトの IfFull == 1 block: 上記の Block == 1と等価。ブロック指定がなければこれがデフォルト値。 noblock: 上記の Block == 0 と等価
PULL
Bit | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
PULL | 1 | 0 | 0 | Delay/side-set | 1 | IfE | Blk | 0 | 0 | 0 | 0 | 0 |
TX FIFO から OSR へ 32-bit ワードを読み込む。
- IfEmpty: 1 なら出力シフトカウントがしきい値 SHIFTCTRL_PULL_THRESH (自動プルと同じレジスタ) になるまで何もしない
- Block: 1 なら TX FIFO が空でストールする。0 なら空の FIFO からプルすると X から OSR にコピーする
ブロックしない PULL のとき、空の FIFO からプルしようとすると、MOV OSR, X と同じ挙動になる。つまり、X から OSR にデータがコピーされる。
ブロックしない場合、デフォルト値を X に指定しておくか、PULL NOBLOCK の後に MOV X, OSR としてデータを戻してやると、最後の FIFO のデータを次のデータまで使用できる。
pull ( ifempty ) pull ( ifempty ) block pull ( ifempty ) noblock ifempty: 上記の IfEmpty == 1と等価。指定がなければデフォルトは IfEmpty == 0 block: 上記の Block == 1 と等価。ブロック指定がなければこれがデフォルト。 noblock: Block == 0 と等価。
MOV
Bit | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
MOV | 1 | 0 | 1 | Delay/side-set | Destination | Op | Source |
- Destination
- 000: PINS
- 001: X
- 010: Y
- 011: Reserved
- 100: EXEC: 命令として実行
- 101: PC: 無条件ジャンプする。
- 110: ISR
- 111: OSR
- Operation
- 00: None: 何もせずに転送
- 01: Invert: ビット反転 (ビット補数)、! または NOT
- 10: Bit-reverse: ビット逆転 (上位と下位を逆転)、 ::
- 11: Reserved
- Source
- 000: PINS
- 001: X
- 010: Y
- 011: NULL
- 100: Reserved
- 101: STATUS
- 110: ISR
- 111: OSR
Source から Destination へデータをコピーする。
MOV EXEC は OUT EXEC と同じ挙動で、レジスタの内容を命令として実行する。
MOV 命令は 1 サイクルで実行され、Source の中の命令が次のサイクルで実行される。
MOV EXEC のディレイは無視されるが、次に実行される命令のディレイは普通通り。
STATUS ソースは EXECCTRL_STATUS_SEL で設定され、FIFO が一杯や空などのステートマシンの状態を示す、すべてのビットが 1 または 0 の値。
MOV は Operation 引数で指定された方法でデータを加工してから転送できる。Invert は Source のビットのそれぞれの 論理 NOT を Destination に送る。
mov <destination>, ( op ) <source> <destination>: 上記の Destination <op>: 指定する場合、 NOT は ! または ~ で指定、ビット逆転は :: で指定。 <source>: 上記の Source
IRQ
Bit | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
IRQ | 1 | 1 | 0 | Delay/side-set | 0 | Clr | Wait | Index |
Index 引数で指定された IRQ フラグをセットまたはクリアする。
- Clear: 1 のとき Index で指定されたフラグを raise させずにクリアする。Clear ビットが指定されたとき Wait ビットは効果なし。
- Wait: 1 のとき raise されたフラグが下がるまで待つ。
- Index
IRQフラグ 0-3 はシステムの割り込みに割り当て可能。
Wait がセットされていると Delay サイクルは、ウェイトが解消されるまで始まらない。
IRQ ではフラグをセットせずにウェイトだけ行えないため、フラグの確認でウェイトだけするなら WAIT 命令を使う。
irq <irq_num> ( _rel ) irq set <irq_num> ( _rel ) irq nowait <irq_num> ( _rel ) irq wait <irq_num> ( _rel ) irq clear <irq_num> ( _rel ) <irq_num> ( rel ): ウェイトする IRQ 番号 (0-7) を指定。rel が指定されていると、 実際の IRQ 番号は、IRQ 番号 (irq_num10) の下位 2 ビットを ステートマシン ID (sm_num10) との和(irq_num10 + sm_num10)に置き換えられる。 irq: ウェイト無しで IRQ をセット irq set: ウェイト無しで IRQ をセット irq nowait: ウェイト無しで IRQ をセット irq wait: IRQ をセット、そのフラグがクリアされるまでウェイト irq clear: IRQ フラグをクリア
SET
Bit | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
SET | 1 | 1 | 1 | Delay/side-set | Destination | Data |
- Destination
- 000: PINS
- 001: X (下位5ビットに Data が書き込まれ、他のビットは 0 にクリアされる)
- 010: Y (下位5ビットに Data が書き込まれ、他のビットは 0 にクリアされる)
- 011: Reserved
- 100: PINDIRS: ピンの向きを設定、1 で出力、0 で入力
- 101: Reserved
- 110: Reserved
- 111: Reserved
- Data: ピンまたはレジスタに書き込まれる直値
直値 Data を Destination に書き込む。
制御信号のクロックやチップセレクト、ループカウンタの初期化などに使用できる。
Data は 5 ビットのデータをスクラッチレジスタに設定でき、0-31 の値は 32 回の繰り返しに使える。
SET および OUT ピンのマッピングは独立している。
set <destination>, <value> <destination>: 上記の値 <value>: 設定される値 (0-31)
疑似命令
nop
MOV Y, Y に置き換えられる。何も起きない。ディレイのみを稼ぐ時やサイドセットなどに使用できる。
MOV Y, Y は Y レジスタの値を Y レジスタに未加工で送るため、実質何も変化しないで 1 サイクル消費する。ディレイやサイドセットは普通の命令と同じ様に指定できる。
動作クロック
基本のシステムクロックを分割して利用する。最小分割数は1、つまりシステムクロックと同値。
- クロックはステートマシンごとに指定可能。
- n: 16-bit 整数
- f: 8-bit 小数部
- 動作クロック = システムクロック/(n + f / 256)
RPi pico の SDK ではデフォルトの動作周波数が 125MHz となっている。大抵はこれをベースに PIO ステートマシンの動作クロックを決める。
命令実行
命令メモリから実行される以外に以下の方法で命令を実行させられる。
- SM0_INSTR (0-3) レジスタに書き込んだ命令は即座に実行される
- MOV EXEC Source、Source から読み込んだ命令を実行
- MOV EXEC OSR なら OUT EXEC と同じ
- 他に X, Y, ISR などがまともなソースとして使える
- OUT EXEC、OSR から読み込んだ命令を実行
MOV EXEC/OUT EXEC 共にその命令が実行されてから、ソースからの命令が実行される。
初期化
ステートマシンの設定はレジスタで行うが、SDK に関数として用意されている。
ピンの入出力の方向や初期値の設定を行う関数も用意されている。関数では SET 命令を実行するため、pioasm で命令を記述したときと同じ結果が得られる。
初期化の命令分だけ命令メモリが節約できる。
自分でコア側から命令を実行させるには、SM0_INSTR レジスタに書き込むと即座に実行される。
節約方法
命令メモリは 32 しかないので、できるだけ命令数を節約したい。以下のような方法がある。