Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[提案] 明示的なノンブロッキング代入 #113

Closed
ryuz opened this issue Jan 31, 2023 · 31 comments
Closed

[提案] 明示的なノンブロッキング代入 #113

ryuz opened this issue Jan 31, 2023 · 31 comments

Comments

@ryuz
Copy link

ryuz commented Jan 31, 2023

以下、賛否のある話だとは思うので、趣旨に合わなければ棄却頂ければと思います。

現在の Veryl では、always_ff で書いた = はノンブロッキング代入、always_comb や function などで書いた = はブロッキング代入に展開されるかと思います。

この際にまったく同じコードが書く場所によって解釈が変わることになるかと思います。

あまり良いコードではないですが、

counter = counter + 1;
pulse   = 0;
if counter >= 1000 {
    counter = 0;
    pulse   = 1;
}

のようなものを、はじめ always_ff の中で書いていて、複雑になってきたので一部 always_comb などに移動させた際に、うっかりカウント数が1つ変わってしまったりします。

1つの言語の中で、同じ記述の意味が変わってしまうのは少し奇妙に感じました。
ブログ記事などにソースコードを一部抜粋する場合など、always_ff の中か、always_comb のなかかわからないと読み違えてしまうと思います。

always_ff の中は従来通り <= のようなブロッキング代入用の文字を割り当てれば安全性が増すように思いました。

ご意見いただけると幸いに存じます。

@dalance
Copy link
Collaborator

dalance commented Jan 31, 2023

確かに賛否ありそうですね。= に統一した理由としては

  • always_ff 中でブロッキング代入を禁止したい
  • ノンブロッキングだけにするなら代入演算子は = に統一できる
  • = にすると += なども使用可能になる

といったあたりです。最後の点を除けば <= でもやりたいことは達成できるかもしれません。

1つの言語の中で、同じ記述の意味が変わってしまうのは少し奇妙に感じました。

SystemVerilog内でも assignparameter= は特にブロッキング代入というわけでもないように思います。
個人的な感覚としては、それらの = の意味はその記号自体というよりは assignparameter で認識していて、
同様に always_ffalways_comb などもそのキーワードで認識しているように思います。
そのため = に統一したとしても特に問題ないだろうという判断でした。

@ryuz
Copy link
Author

ryuz commented Jan 31, 2023

ありがとうございます。理由をお聞きして私も悩ましく思い始めております。

assign や parameter の = は特にブロッキング代入というわけでもないように思います。

確かにおっしゃる通りですね。
ただ assign や force などの = や、初期化の = は式の中で判断できるとおもいますが、always_ff は文として、下手をするとエディタの画面外に出ている部分まで見に行かないとどっちなのか判断できない点で少しスコープの大きさが違うような印象を受けました。
うっかりバグを書きにくい言語であってほしいという思いはあるので個人的には += を諦めても、ノンブロッキング代入の明示化はアリな気がしています。

強く主張するつもりはありませんが、興味深く思った部分なので、他の方の意見なども伺ってみたいところではあります。

@ryuz
Copy link
Author

ryuz commented Jan 31, 2023

ちなみに、ちょっと違う話になるかもですが、always_ff の中で

  1. 一旦すべて automatic なテンポラリ変数に入れる
  2. テンポラリ変数でブロッキング代入で演算する
  3. 末尾で一括してノンブロッキング代入する

という流れに変換してしまえば、すべてブロッキング代入のポリシーで書ける言語にもなりそうな気がしたのですが、そういった形で文法を統一できるような可能性はありますでしょうか?
(素人の浅知恵なので穴がありそうな気はしていますが)

@taichi-ishitani
Copy link
Contributor

  • 「FF記述中でブロッキング代入を使用」というポカミス除け
  • 生成後のSVの読みやすさ

を考えると、自分は今のままの仕様が良いですね。
FF記述中でも+=を使いたいので、代入演算子の変更は反対の立場です。

@ryuz
Copy link
Author

ryuz commented Jan 31, 2023

ちょっと自信はないのですが

  • テンポラリ変数&末尾ノンブロッキング代入の構文を always_ff とは違う名前で新たに作り、ブロッキング代入以外禁止する
  • ノンブロッキング代入演算子は <= を定義して always_ff ではノンブロッキング代入以外禁止する

というような事はありえますでしょうか?

ノンブロッキング代入の存在自体が話をややこしくしているような気はしているので、すべてブロッキング代入だけで書ける文法は、実現可能なら有難く思います。

(実際 always_ff は代入のみとして always_comb に処理を切り出すこともあります。ただ、今の値と次の値の2変数作るのでごちゃごちゃしてしまい、ここが隠れるノンブロッキング代入は便利ではあるのですが、Veryl が隠してくれるなら解決するような気もしました)

Veryl から生成した SV をさも最初から SVで書いた体で納品したいとかはありそうな気がしているので、always_ff も欲しいところです。

@dalance
Copy link
Collaborator

dalance commented Jan 31, 2023

always_ff は文として、下手をするとエディタの画面外に出ている部分まで見に行かないとどっちなのか判断できない点で少しスコープの大きさが違うような印象を受けました。

個人的なコーディングスタイルとしては大きなalways_ffは回避していますね。
理由は一般的なプログラミング言語と同様に「大きな関数は可読性が低い」というのもありますし、always_ff固有の話としてクロック種別とリセット有無、同期か非同期かは常に把握しておく必要があるからです。

ブロッキングで統一というのはChiselがそういう感じであったと思います。
Chiselの大規模なコードをデバッグした経験では、この点はかなりバクの温床であるように感じました。
FFには1クロックに1回しか代入できないというのはかなり本質的な制限なので、言語側で複数回代入できるように見せかけるのはあまり筋が良くないように思います。
(ソフトウェアエンジニアが取っ付きやすいというのはあるかもしれませんが、初心者でも最終的には理解して欲しいという気持ちです)

@ryuz
Copy link
Author

ryuz commented Jan 31, 2023

FF記述中でも+=を使いたいので、代入演算子の変更は反対の立場です。

これも質問で恐縮なのですが、 <+= みたいなノンブロッキング専用の += とかってあり得る話なのでしょうか?

時間が進むまで変化しない変数と、同じ時刻の中で参照内容が変わる変数が、変数定義でも式でも見た目区別できないのはそれなりに危険な気もしております。

@dalance
Copy link
Collaborator

dalance commented Jan 31, 2023

これも質問で恐縮なのですが、 <+= みたいなノンブロッキング専用の += とかってあり得る話なのでしょうか?

シフト代入演算子<<=があるので簡単ではないですね。
<=ではなく@=などにすれば回避できますが。

@taichi-ishitani
Copy link
Contributor

仕様の簡潔さを優先するなら、演算子の追加はしないほうが良いのではないでしょうか?

@ryuz
Copy link
Author

ryuz commented Jan 31, 2023

ブロッキングで統一というのはChiselがそういう感じであったと思います。
Chiselの大規模なコードをデバッグした経験では、この点はかなりバクの温床であるように感じました。

ありがとうございます。Chisel は興味はあるのですが手が出せていなかったので大変参考になります。

FFには1クロックに1回しか代入できないというのはかなり本質的な制限なので、言語側で複数回代入できるように見せかけるのはあまり筋が良くないように思います。

ここは SV の時点で賛否があるところだとは理解しております。
ただ、複数回代入が便利な場合もあって、私はついついSVで使ってしまっています。

(ソフトウェアエンジニアが取っ付きやすいというのはあるかもしれませんが、初心者でも最終的には理解して欲しいという気持ちです)

Chisel 風にしたい場合、 いっそ always_ff では単純代入しかしないことにして、function なりに処理を小分けに切り出していくコーディングスタイルの方が安全なのかもしれませんね。

@ryuz
Copy link
Author

ryuz commented Jan 31, 2023

シフト代入演算子<<=があるので簡単ではないですね。
<=ではなく@=などにすれば回避できますが。

ありがとうございます。よく理解できました。

仕様の簡潔さを優先するなら、演算子の追加はしないほうが良いのではないでしょうか?

私も増やすべきでないと思っています。

@ryuz
Copy link
Author

ryuz commented Jan 31, 2023

always_ff や文法は今のままにするとして、

  • テンポラリ変数に今の値をコピーしてブロッキング代入で処理して末尾でノンブロッキング代入してくれる文

という糖衣構文があると、ソフトウェアエンジニアが取っ付きやすい気はしています。
マクロがあればそちらで実現する手もありそうな気がしていますが、このあたり如何でしょうか?

@taichi-ishitani
Copy link
Contributor

taichi-ishitani commented Jan 31, 2023

ソフトウェアエンジニアが取っ付きやすい気はしています。

私としては、Verylが書きやすいSVを目指すなら、そこを対象者に含めるのは疑問符が付きますね。

@dalance
Copy link
Collaborator

dalance commented Jan 31, 2023

two process methodというものが昔から提案されていて
これで書いている人はそれなりにいるようです。

https://qiita.com/ryo_i6/items/cf064e816bfdb7babb81

ソフトウェアエンジニアの人はこのあたりからだと入りやすいと思います。
糖衣構文で隠してしまうとそれで慣れていまい、上級者の書いたコードを読めずにレベルアップし辛いという問題もあるかもしれません。

@ryuz
Copy link
Author

ryuz commented Jan 31, 2023

私としては、Verylが書きやすいSVを目指すなら、そこを対象者に含めるのは疑問符が付きますね。

なるほど、私がソフト系の書き方を期待してしまったのがちょっと趣旨違いだったかもしれません。

そもそも論として Verilog の reg が FF にも ワイヤーにもなりえるのも混乱の始まりな気もしています。
Veryl 内での文法チェックだけに使う、ff を意図した変数か wire を意図した変数かを明示できる属性が付与できるとかがあってもいいのかもしれません。そうすると文法でなく変数属性の方で文意が明確になりそうな気がしますし、「うっかりコピー」でのバグも減りそうな気がしました。

またいっそのこと always_ff 内での代入を一回に制限するオプションをつけるというのもありなのかもしれません(その場合も代入した後に変数参照しても古い値が読まれるので、どこに書くかで文意が変わるのは同じですが)

Veryl を 「書きやすいSV」 として定義して、それ以上のことをしたければ「さらに別言語かぶせたりマクロで何とかする」というポリシーであればそれはそれでわかりやすい気がいたしました。
(Rust と同レベルのマクロ欲しいです)

@ryuz
Copy link
Author

ryuz commented Jan 31, 2023

two process methodというものが昔から提案されていて
これで書いている人はそれなりにいるようです。

古いVerilogの頃から同じことはしていましたが、名前がついている技法だったのですね。ありがとうございます。

ソフトウェアエンジニアの人はこのあたりからだと入りやすいと思います。

もろに私がそうで、Veryl で、その two process method で書こうとして、今回違和感を感じた次第でして。

糖衣構文で隠してしまうとそれで慣れていまい、上級者の書いたコードを読めずにレベルアップし辛いという問題もあるかもしれません。

ここは賛否ありそうな気がします。
別の話として、マクロで書ける糖衣構文に関してはマクロで書くべきな気はしております。

@taichi-ishitani
Copy link
Contributor

taichi-ishitani commented Jan 31, 2023

FF記述中にも右辺値として組合せ回路記述ができる事がそもそもの要因ですから、「always_ffの中は代入以外(制御構文含む)使用禁止(two processの強制)」というルールを課して、コーディングルールチェックする方が良さそうに思います。

言語側でどうこうするより、ルールとか言語外でどうこうする領域なのかなと言う考えです。

@taichi-ishitani
Copy link
Contributor

always_ff は文として、下手をするとエディタの画面外に出ている部分まで見に行かないとどっちなのか判断できない点で少しスコープの大きさが違うような印象を受けました。

これも、ブロック中の最大行数を決められるようにして、コーディングルールチェックで弾く問題のように思います。

@taichi-ishitani
Copy link
Contributor

taichi-ishitani commented Feb 1, 2023

always_ffとalways_combの字面が似ていて、それが混乱の元ならば、それぞれのキーワードを変更するのも一手でしょうか?
例えば、ffとcombinationとか

@dalance
Copy link
Collaborator

dalance commented Feb 1, 2023

あとはそこまで積極的な理由でもないですが、代入演算子は式構文の奥深くにいるので
<= に変更するとなると式構文全体を2重に持つことになって実装複雑性はかなり増しますね。
(構文レベルでは =<= の両方を許容して意味解析レベルで弾くのもありですが、結局複雑性の増加に変わりはないです)
なので <= と明示するメリットがかなり明白でない限りは避けたいところです。

まだ実装してないですが、オプショナルなlinterルールでプロジェクト毎に
方針を決めて書くことで緩和できるならその方がいいかもしれません。

@taichi-ishitani
Copy link
Contributor

taichi-ishitani commented Feb 1, 2023

あくまでもHDLですから、言語仕様はRTLエンジニアを主使用者に添えるべきで、SWエンジニアがミスしそうな部分はコーディングルールなどで縛る方が良いように思います。

@ryuz
Copy link
Author

ryuz commented Feb 1, 2023

いろいろなご意見有難うございます。背景にある思想がだいぶ理解出来てきました。
本提案は棄却頂くのが妥当かと考えております(毎回お騒がせして申し訳ないです)。

個人的に <=予約=即時 と読む癖がついていたので、両者同じ書き方で戸惑ったという部分が多かったですが、慣れの問題かもしれません。

私の思いつく範囲の Verilog-HDL の嫌なところとして

  • 宣言しなくても勝手に 1bit の wire が出来てしまう
  • 変数が FF になるか wire になるかが文脈依存である(明示するのにハンガリー記法とかで書いたことも...)
  • 幅ゼロのバスが定義できない
  • function が parameter を取れない
  • 一時変数でうっかりラッチが出来てしまう危険がある(SVだとautomaticつければよい)
  • ブロッキング代入する変数は評価順序に注意が必要
  • 変数の初期化漏れを拾う確実な方法がない
  • bit幅の不一致関連

などがあり、SVでも互換性の観点から一部引きずっているかと思います。

Lint で解決できるものもありますが、 Veryl ならほぼすべて解決しそうに思えております。

今回、代入先の変数が FF か wire か見分けやすくする点で考えた次第でしたが浅知恵でした。

@dalance
Copy link
Collaborator

dalance commented Feb 1, 2023

変数が FF になるか wire になるかが文脈依存である(明示するのにハンガリー記法とかで書いたことも...)

これは個人的には(無意味と批判されたシステムハンガリアンではなく)適切なアプリケーションハンガリアン記法だと思っています。

https://ja.wikipedia.org/wiki/%E3%83%8F%E3%83%B3%E3%82%AC%E3%83%AA%E3%82%A2%E3%83%B3%E8%A8%98%E6%B3%95#%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%8F%E3%83%B3%E3%82%AC%E3%83%AA%E3%82%A2%E3%83%B3

タイミングを気にする設計の場合、各変数がFFなのか入出力なのかwireなのかを継続的に把握することが重要で、変数名に r_ w_ i_ o_ などを付けて区別しています。
例えば以下のコードを見るだけで「後者に比べて前者はタイミングが厳しい可能性がある」と読めます。

assign w_mul = w_x * w_y;
assign w_mul = r_x * r_y;

この用途に関してはLSPなどでの型名のホバーではちょっと不足で、変数名の方がいいかな、と思っています。

@ryuz
Copy link
Author

ryuz commented Feb 1, 2023

タイミングを気にする設計の場合、各変数がFFなのか入出力なのかwireなのかを継続的に把握することが重要で、変数名に r_ w_ i_ o_ などを付けて区別しています。

ありがとうございます。 入出力も区別されるのですね。参考になります。

レジスタ(FF)を意図した変数は always_ff の中でしか代入しない(逆もしかり)なルールが運用上担保できれば安心感のあるコーディングはできそうに思いました (コンパイラではなく、コーディングルールに対する Lint系 の話ですかね)。

ちなみに組み込みソフトでもメモリマップドI/Oのレジスタに対応する変数などは、レジスタ幅や符号などをハンガリアンで記述するのは結構効果があったように思います。ハードウェア制約のある部分とは相性が良いのかもしれませんね。

@taichi-ishitani
Copy link
Contributor

taichi-ishitani commented Feb 1, 2023

レジスタ(FF)を意図した変数

変数宣言上は両者の区別はないので、ルールとしては、

  • FF 用/組み合わせ用で、接頭辞を分ける
  • always_ff 中で複数回の代入を行わない
    • AST の走査で確認できる?
    • 代入以外禁止の方が実装は楽か?

になるでしょうか?

@dalance
Copy link
Collaborator

dalance commented Feb 6, 2023

lintのルールとしては always_combassign の左辺値の接頭辞と always_ff の左辺値の接頭辞ということでいいかと思います。always_ff 中の時点でFFであることは確定なので。

複数回代入の禁止などはネーミングルールとは別の合成不能記述のチェックのような感じでしょうか。
こちらはlintではなくsemantic errorでいいと思います。

@taichi-ishitani
Copy link
Contributor

合成不能記述のチェック

合成不可ではないので、lint error のほうが良いのではないでしょうか?
(お行儀が悪いのは確かですが。)

#86 があるとは言え、

a      = b;
a[1:0] = '0;

のような記述がエラーになるのは厳しいのではないでしょうか。

@dalance
Copy link
Collaborator

dalance commented Feb 6, 2023

これって合成可能なんでしたっけ?
2つの代入のうちどちらを採用すべきかわからない気がするのですが。
(後者と決まっているんでしたっけ?)

always_ff @ (posedge i_clk) begin
    a <= b;
    a[1:0] <= '0;
end

@taichi-ishitani
Copy link
Contributor

LRM 的には、ノンブロッキング代入が実行された順に、左辺の更新が実行されるので、a[1:0] <= '0; が最終的な結果になりますね。
DC/Vivdado もそのように合成します。

image

@dalance
Copy link
Collaborator

dalance commented Feb 6, 2023

それは知りませんでした。
であればlintですね。

@dalance
Copy link
Collaborator

dalance commented Feb 7, 2023

lintルールの追加は別issueにしたのでこちらは閉じます。

@dalance dalance closed this as completed Feb 7, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants