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

[提案] signed 型の部分取り出し時の扱い #94

Closed
ryuz opened this issue Jan 22, 2023 · 39 comments
Closed

[提案] signed 型の部分取り出し時の扱い #94

ryuz opened this issue Jan 22, 2023 · 39 comments

Comments

@ryuz
Copy link

ryuz commented Jan 22, 2023

SystemVerilog では

logic signed a[7:0][3:0]; ← (間違っておりました)

logic signed [7:0][3:0] a;

のような signed な 32bit の変数を a[1] のように4bit だけ切り出すようにすると unsigned になってしまうようです。

例えば下記などにもその話があります。

https://stackoverflow.com/questions/57216757/is-signed-type-valid-for-each-element-using-typedef-packed-array-v-s-multidi

Verly では Rust 的な構文となっているので、例えば自動で $signed(a[1]) のように $signed で囲まれるようにするなど如何でしょうか?

@taichi-ishitani
Copy link
Contributor

どちらかというと、unpacked arrayに対応させる方が良いのではないでしょうか?

@dalance
Copy link
Collaborator

dalance commented Jan 22, 2023

#93 と併せてpacked/unpackedの区別ができればいいということになるかと思います。
logic/bit以外の[]がunpackedなので、[]はunpackedに統一して
別途packedを表現できるようにするのがいいでしょうか。
「logic/bitは型引数としてビット幅を取れる」という考え方でいくとこんな感じですかね。

var a: signed logic(4) [8];
var a: signed logic(4, 4) [8];
var a: signed logic(4)(4) [8];

var a: signed logic<4> [8];
var a: signed logic<4, 4> [8];
var a: signed logic<4><4> [8];

@ryuz
Copy link
Author

ryuz commented Jan 22, 2023

packed/unpacked の区別ができる構文は非常に汎用性が高いように思いました。

あと、あえて言うなら、古い Verilog 表記のモジュールや、ハードマクロに packed 化してそのまま入れたい時があるので、packed へキャストする方法もあるとありがたいです。

あと、少し違う話なのですが Verilator が

a[1] のように4bit だけ切り出すようにすると unsigned になってしまう

を、signed と解釈してしまうようで、合成器との不一致で悩んでいたりします。
仕方なく常に $signed(), $unsigned() を明示して使っていたりするのですが、Veryl -> SV するときにそういうオプションがあると嬉しいです。

他にもローカル変数定義で Vivado Sim が デフォルトで automatic で、 Vivado Synthesizer が static 解釈なので不一致が起こったりと、各ツールのバグや方言や実装依存で振り回されることもあります(Verilog上は static が正しいのだと思いますが、モダン言語的には automatic であってほしいところでして)。
Veryl の SVの出力が、なるべくデフォルト部分の省略はせずになるべくなんでも明示した出力であると嬉しいです。

@taichi-ishitani
Copy link
Contributor

var a: signed logic<4, 4> [8];が良いですね

@taichi-ishitani
Copy link
Contributor

taichi-ishitani commented Jan 22, 2023

module test;
  bit signed [1:0][1:0] a;
  bit signed [1:0] b[2];
  
  initial begin
    a[0] = '1;
    b[0] = '1;
    $display("%d %d", a[0], b[0]);
    #1;
    $finish;
  end
endmodule

の大手三社のシミュレータでの実行結果は、

3          -1

になりますし、LRM には、

—  Bit-select results are unsigned, regardless of the operands. 
—  Part-select results are unsigned, regardless of the operands even if the part-select specifies the entirevector. 

とあるので、a の宣言が logic signed [3:0][7:0] a ではなく logic signed a[7:0][3:0] ならば、Verilator の挙動が間違っていそうではあります。

@taichi-ishitani
Copy link
Contributor

logic signed a[7:0][3:0] ですと、「1ビットの符号付変数」の配列ということになりますが、意図した記述なのでしょうか?

@ryuz
Copy link
Author

ryuz commented Jan 22, 2023

Verilator で試すと

-1 -1

になってしまいますね。

Verilator の挙動が間違っていそうではあります。

私も多分そうだとは思っております。

@taichi-ishitani
Copy link
Contributor

logic signed a[7:0][3:0] ですと、「1ビットの符号付変数」の配列ということになりますが、意図した記述なのでしょうか?

という宣言も可能になってしまうので、「符号付の場合、幅は2ビット以上」というチェックを入れても良さそうですね。

@ryuz
Copy link
Author

ryuz commented Jan 22, 2023

logic signed a[7:0][3:0] ですと、「1ビットの符号付変数」の配列ということになりますが、意図した記述なのでしょうか?

すみません

logic signed [7:0][3:0] a;

の間違いです。

@ryuz
Copy link
Author

ryuz commented Jan 22, 2023

「符号付の場合、幅は2ビット以上」

DeepLearning などで INT1 はありえるので、文法上何らかの誤解のない符号付き 1bit の宣言方法もあると良いように思いました。

logic signed [0:0] a [7:0][3:0];

の [0:0] に来る部分を省略できないようにするとか如何でしょう。

@dalance
Copy link
Collaborator

dalance commented Jan 22, 2023

var a: signed logic<4, 4> [8];が良いですね

私もそれがいいと思ったのですが、よく考えると <EXPRESSION>EXPRESSION 中に比較演算子 > があるとおそらくLL(k)でパース不能ですね。 次点でlogic(4, 4) でしょうか。

signed 1bitについては -1~0の範囲を正しく表現できるので、不正扱いする必要はないように思います。

var a: signed logic [8];
var a: signed logic(1) [8];
var a: signed logic(4) [8];
var a: signed logic(4, 4) [8];
logic signed a [8];
logic signed [0:0] a [8];
logic signed [4-1:0] a [8];
logic signed [4-1:0][4-1:0] a [8];

となるので、1bitであることを明示したい場合は logic(1) とすればいいのではないでしょうか。

@taichi-ishitani
Copy link
Contributor

taichi-ishitani commented Jan 22, 2023

logic/bitは型引数としてビット幅を取れる

packed であれば、任意の型でpacked arrayにできますし、logic/bitに限定する必要はないのではないでしょうか?

@dalance
Copy link
Collaborator

dalance commented Jan 22, 2023

packed であれば、任意の型でpacked arrayにできますし、logic/bitに限定する必要はないのではないでしょうか?

元々の考えとしてはSVで任意の型とpackedとunpackedを組み合わせ可能なのは自由度が高すぎて、混乱を招いている気がしたので制限する方向でした。
なので制限を解除するには理由がほしい感じがしています。
例えばlogic以外のpacked に対応しないと困るケースってどういうものがありますか?

@taichi-ishitani
Copy link
Contributor

例えばlogic以外のpacked に対応しないと困るケース

無いと絶対に困ると言うほどではないですが、

  1. Verilog/VHDL との相互運用性
    • 論理合成後のネットリスト上での取り扱いも含む
  2. function の戻り値にする際に、typedef が不要

ですね。

1.に関しては、SV 出力時の配列のフォーマットを選択できるようにすれば、事足りるのかもしれません。
(適宜、$signed/signed'を補う必要はありますが。)

RgGen では Verilog との相互運用性を要望された際に、配列のフォーマットを、

  • packed 配列で出力
  • unpakced 配列で出力
  • ビット列に詰めて出力

から選べるようにして、対応しました。

@dalance
Copy link
Collaborator

dalance commented Jan 22, 2023

なかなか難しいですね。
packed/unpackedをSV仕様のまま露出すると、言語仕様としてきちんと定義するのは難しくなる気がします。
(現状でもSVの仕様書に書いてあることと各処理系の解釈はまちまちなので)
「SVのpacked/unpackedです」と言ってしまうのが楽ではありますが…。

@dalance
Copy link
Collaborator

dalance commented Jan 22, 2023

よく考えると #93 のような挙動はどのみち制御不能なので
単純に構文だけ整理した形でいい気がしてきました。
後で確認しますが曖昧性の問題がなければ var a: struct_t(4, 4) [8] で行こうと思います。

@ryuz
Copy link
Author

ryuz commented Jan 22, 2023

後で確認しますが曖昧性の問題がなければ var a: struct_t(4, 4) [8] で行こうと思います。

ご検討頂きありがとうございました。

@dalance
Copy link
Collaborator

dalance commented Jan 23, 2023

やはり () は関数呼び出しとの曖昧性が問題になりそうです。
例えば array を [[]] にするとかはどうでしょうか。
多次元が結構見づらい気もしますが、, 区切りならそこまででもないかも?

var a: logic [4][4] [[8]][[8]];
var a: logic [4, 4] [[8, 8]];

@dalance
Copy link
Collaborator

dalance commented Jan 23, 2023

そもそも packed を [] で表現するのがダメですね…。
EXPRESSION中のpart selectと packed typeが区別できないです。

@dalance
Copy link
Collaborator

dalance commented Jan 23, 2023

既存言語における <> の曖昧性解決

https://zenn.dev/qnighy/scraps/34d96c787a758d

このうち今回適用できそうなのはRustのやり方(式中に出現する <>::<> にする)

var a: logic <4, 4> [8][8];
parameter TYPE: type = logic::<4, 4>;

既存言語には存在しないが、比較演算子側を妥協すると

assign a = 1 <: 1; // 1 < 1
assign a = 1 <= 1;
assign a = 1 >: 1; // 1 > 1
assign a = 1 >= 1;

var a: logic <4, 4> [8][8];

長さがそろうのはいい気もしますが、さすがに違和感が…。

式中に突然型が出現することはtype parameterくらいしかない(キャストは as が前置なので曖昧性はない)
のでRust方式がいいですかね。

@dalance
Copy link
Collaborator

dalance commented Jan 23, 2023

Rust方式は解決になってないですね…。
曖昧性は型引数に含まれる ><> の終端にもあるので(Rustは型引数に > が含まれない)
使えるとすると、[[]] 方式にしたときに発生する式中のpart selectとpacked typeの曖昧性解決でした。

var a: logic [4, 4] [[8, 8]];
parameter TYPE: type = logic::[4, 4];

比較演算子 <: が意外とましなのかも?

@taichi-ishitani
Copy link
Contributor

<=と文字数が揃うので、<:は良いのではないでしょうか?

@dalance
Copy link
Collaborator

dalance commented Jan 23, 2023

既存言語にないので違和感ある気がしていましたが、案外良いかもしれませんね。
他に良い案がなければそれにしようと思います。

@ryuz
Copy link
Author

ryuz commented Jan 23, 2023

字句解析に知見が無く素人意見で大変恐縮なのですが、演算子には C や Rust を継承してほしい気はします(初見ユーザーが混乱しそうに思いました)。

例えばですが module や function の parameter 指定みたいに

var a: signed logic #(4, 4) [8];

みたいな記法とか、

var a: signed logic [#4, #4, 8];

みたいに添え字の方に packed, unpacked を区別する何らかのプリフィックスを付けるとかは難しいのでしょうか?

あと、本家 Verilog がやってるように begin, end を使うことで {} を空けるとかはありえるのでしょうか?

素人質問ばかりですみません。 <: も悪くはないのだと思いますが、ちょっと面食らってしまった印象があったので。

@dalance
Copy link
Collaborator

dalance commented Jan 23, 2023

logicのpacked幅は最もよく使う記法なので#() だとちょっと長く感じてしまいます。
prefixを付けるならunpackedの方かな、と思いますが記号の選択がちょっと難しいですかね。
(あと記号方式にすると logic [#4, 8, #4] などと書けてしまうのもありますかね)

() とRust方式の組み合わせがいいでしょうか。::があれば関数呼び出しとの区別はできるので。

var a: signed logic (4, 4) [8];
parameter TYPE: type = logic::(4, 4);

@ryuz
Copy link
Author

ryuz commented Jan 23, 2023

logicのpacked幅は最もよく使う記法なので#() だとちょっと長く感じてしまいます。
prefixを付けるならunpackedの方かな、と思いますが記号の選択がちょっと難しいですかね。

なるほど。私個人としても Verilog の延長色が強いので特に合成可能記述では unpacked は殆ど使わないのですよね。
ただ、生成されるインスタンスの静的特性を指定する点では #() は理解はしやすい気はしました。

(あと記号方式にすると logic [#4, 8, #4] などと書けてしまうのもありますかね)

確かにですね。 logic [4, 8; 4] とか、何かセパレータを置く方がまだ自然かもしれませんね。

() とRust方式の組み合わせがいいでしょうか。::があれば関数呼び出しとの区別はできるので。

Rust 風に統一できるのであれば、個人的には一番嬉しいです。
ただ、HDL の良い部分(というか必要な部分)だけをうまく Rust に足そうとすると、記号が足りないところがいろいろあるのは想像に難くないです。

@taichi-ishitani
Copy link
Contributor

変数宣言のところも、logic::(4, 4)でも良さそうに思うのですが、いかがでしょうか?

@dalance
Copy link
Collaborator

dalance commented Jan 24, 2023

大量に出てくる構文なので #() でも長いかな、と思っていましたがそうでもないですか?
あとフォーマッタで () を縦方向にそろえると、:: の前後が分かれてしまってちょっと意味が取りずらい感じになる気がします。

module ModuleA (
    a: input  logic   (10),
    b: input  struct_t(10),
    c: input  logic   (10),
    d: input  logic   (10),
    e: output logic   (10),
) {
    var x: logic (10);
    var y: logic (10);
    var z: enum_t(10);
}
module ModuleA (
    a: input  logic   ::(10),
    b: input  struct_t::(10),
    c: input  logic   ::(10),
    d: input  logic   ::(10),
    e: output logic   ::(10),
) {
    var x: logic ::(10);
    var y: logic ::(10);
    var z: enum_t::(10);
}
module ModuleA (
    a: input  logic::   (10),
    b: input  struct_t::(10),
    c: input  logic::   (10),
    d: input  logic::   (10),
    e: output logic::   (10),
) {
    var x: logic:: (10);
    var y: logic:: (10);
    var z: enum_t::(10);
}

@ryuz
Copy link
Author

ryuz commented Jan 24, 2023

(この issue の趣旨ではないかもですが、Veryl の フォーマッタ試してみて、縦に綺麗に揃うのはとても素晴らしいなと思いました。)

以下、素人意見なのですが、

ユーザーにもよるのかと思いますが、特に合成用の記述に関しては packed が主役(packedは配列というよりバス幅の多次元なイメージ持っています)で、めったに unpack は出てこないので、正直 packed に一等地の記号を割り当ててほしい気はします。

一方で、

logic/bit以外の[]がunpackedなので

というのが言われてみると、その通りで、int の配列のようなものが Rust と同じに書けないのも嫌な感じがします。
packed が型の一部である感覚は強いので、思想的には

var a: logic <4, 4> [8][8];

に近いものを混乱ない文法で書ければしっくりくる気はします。
(logic と bit に対してだけ何かストレスにならない付加情報を付けられるいい書き方があればよいのでしょうが...)

個人的に Veryl に期待している部分として

  • SVの各種課題を補完する美しい言語であってほしい
  • 「Rust は書いたことあるけど RTL は初めて」という人にもとっつきやすい言語に育ってほしい

というのがあります。SV を書く人にもとっつきやすいに越したことはないのですが、Veryl の現在の文法を見ていると、SV の思想よりは Rust の思想を優先した方が良い方向に行きそうに感じました。

@dalance
Copy link
Collaborator

dalance commented Jan 24, 2023

どの括弧を使うかで再度まとめてみました。
結局式文脈での曖昧性はどれでも発生するのでRustのturbofish方式は必須ですね。

()

var a: signed logic (4, 4) [8][8];
var b: enum_x (4, 4) [8][8];
inst: interface_y [8][8];
parameter TYPE: type = logic::(4, 4);
  • packedが() でunpackedは []
  • (4, 4) は関数呼び出しに見える(特にユーザ定義型)
  • 式文脈でtypeが出てくる場合に関数呼び出しとの曖昧性が発生するので ::() にする必要がある

<>

var a: signed logic <4, 4> [8][8];
var b: enum_x <4, 4> [8][8];
inst: interface_y [8][8];
parameter TYPE: type = logic::<4, 4>;
  • packedが<> でunpackedは []
  • <4, 4> は型変数に見える
  • <> の中に比較演算子が出てくると曖昧性が発生するので解決の必要あり
    • 比較演算子を変更する <: >:
    • <> 内では比較演算子を使用不可にする
    • <> に終端記号を付与 <># のような
  • 式文脈でtypeが出てくる場合に比較演算子との曖昧性が発生するので ::<> にする必要がある

[]

var a: signed logic [4][4] #[8][8];
var b: enum_x [4][4] #[8][8];
inst: interface_y #[8][8];
parameter TYPE: type = logic::[4][4];
  • packedが[] でunpackedは #[]
  • interfaceの配列がちょっと長くなる(が許容範囲内か)
  • 式文脈でtypeが出てくる場合にpart selectとの曖昧性が発生するので ::[] にする必要がある

@taichi-ishitani
Copy link
Contributor

個人的な好みですが、

  • packed/unpacked を同じカッコの組みで扱わない
    • packed は型の一部のイメージなので
    • packed: <>
    • unpacked: []
  • 比較演算子とのあいまいさ解決のために、比較演算子を <: にするのは可
    • <= と文字数が揃うので、むしろ、これに関係なく <: に変更してほしい
  • 宣言の時と、式の時とで表記は同じにしたいので、常に :: は在ってほしい
  • <4,4>[4][4] とで表記が違うのも違和感があるので、両方とも , 区切りでの方が良いのでは

になると良いですね。

var a0 logic;
var a1 logic::<4>;
var a2 logic::<4, 4>;
var a3 logic::<4, 4> [4, 4];
parameter type A = logic::<4, 4>;

@ryuz
Copy link
Author

ryuz commented Jan 24, 2023

どの括弧を使うかで再度まとめてみました。

ありがとうございます。結構、好みが出てくる感じの判断ポイントなのでしょうか。
まとめだけ見ると () も制約事が少ないように見えました。

比較演算子を <: にする

これもよさそうには思っていますが、一つ質問があり、曖昧性の発生しない文脈では <: を < と書くのは許されたりするのでしょうか? それとも一般的な言語設計として、そういうことはするべきではなく、すべて <: と書くようにすべきものなのでしょうか?
ここは個人的な好みだとは思うのですが、いろんな言語を渡り歩きながらプログラムすることが多いので、コード書く側としては文脈上許されるところでは < も使えると混乱が減って嬉しいです(毎回のように付け忘れそうな気がしています)。

何れの場合でも、文字を揃えたいケースはよくわかるので < を <: とも書ける仕様は嬉しそうな気はします。
ただ Veryl の場合 veryl fmt コマンドで揃えてもらえればそれで良いという人も一定数いそうな気がします。
(ちなみに 私の場合、 Veryl で書いて SVに変換して SV を納品 とかしそうな気がしているので、SV のフォーマットの方が気になっていたりもします。)

@dalance
Copy link
Collaborator

dalance commented Jan 25, 2023

これもよさそうには思っていますが、一つ質問があり、曖昧性の発生しない文脈では <: を < と書くのは許されたりするのでしょうか? それとも一般的な言語設計として、そういうことはするべきではなく、すべて <: と書くようにすべきものなのでしょうか?

一般的に文脈によって切り替わるようなことは主に後方互換性維持のために行われています。ただそのようなことをするとパーサ実装が複雑化するので、新規設計の言語で積極的に採用するべきではないと思います。
特に比較演算子は式が現れるあらゆる箇所に影響するので、処理系全体が複雑化してしまい「SVの構文を単純化する」という当初の目的から外れてしまうと思います。
なので packed にどの括弧を使うとは関係なく、もし <: を導入するのであれば全て <: にする必要があります。

ここは個人的な好みだとは思うのですが、いろんな言語を渡り歩きながらプログラムすることが多いので、コード書く側としては文脈上許されるところでは < も使えると混乱が減って嬉しいです(毎回のように付け忘れそうな気がしています)。

私も多言語を行ったり来たりするので懸念は分かります。
これに関してはツールで補うという考え方もあるかな、と思いました。
language serverでリアルタイムに構文チェックができるので、 : をつけ忘れた場合は式が完成した時点で確認できます。
また、 < の入力に対して <: <= を補完候補として出すのもいいかもしれません。

ここまでの議論と私自身の好みを加味して以下の2案が妥当かな、と思います。

<: >: を採用する場合

曖昧性は根本的になくなるので ::<> も不要です。
比較演算子を許容できるならこれが一番きれいだと思います。

var a: signed logic<4, 4> [8, 8];
var b: enum_x<4, 4> [8, 8];
inst: interface_y [8, 8];
parameter TYPE: type = logic<4, 4>;

<: >: を採用しない場合

::() を常に適用するかどうかは議論の余地があります。

var a: signed logic(4, 4) [8, 8];
var b: enum_x(4, 4) [8, 8];
inst: interface_y [8, 8];
parameter TYPE: type = logic::(4, 4);

@taichi-ishitani
Copy link
Contributor

if 文の構文とっても、各言語でいろいろ違いがありますが (else if/elsif/elseif など)、言語の切り替わりの際に手が滑る程度で、大きな問題なく切り替えできますし、比較演算子が <: になっても、大きな問題にはならないのではないでしょうか?

@taichi-ishitani
Copy link
Contributor

個人的な好みは <: 採用案ですが、仕様と実装が簡潔になる方を採用のが良いのではないかと思います。

@ryuz
Copy link
Author

ryuz commented Jan 25, 2023

なので packed にどの括弧を使うとは関係なく、もし <: を導入するのであれば全て <: にする必要があります。

なるほど、よくわかりました。勉強になりました、ありがとうございます。

仕様と実装が簡潔になる方を採用のが良いのではないかと思います。

私も同じ意見です。

好みの問題になるのであれば、言語作者の方の好みを優先頂くのが作者特権ではないかと思います。
(個人的好みは ::() の方ですが)

@dalance
Copy link
Collaborator

dalance commented Jan 25, 2023

たくさんのご意見ありがとうございます。
結論としては <: を採用することにしようと思います。
見た目に関してはそれぞれ一長一短ありますが、

  • 実装の観点では例外規則が最も少ない
  • ここで <> を正式な括弧として扱えるようにしておくと将来的な拡張でも役立つ

の2点を重視したいと思います。特に後者は今やっておかないと、将来 <> をどうしても入れたくなったときに他言語のような強引なハックをせざるを得なくなるので。

@ryuz
Copy link
Author

ryuz commented Jan 25, 2023

我儘な issue にご対応いただきありがとうございました。大変勉強になりました。
方針が固まって良かったです。
引き続き楽しみにしております!

@dalance
Copy link
Collaborator

dalance commented Jan 27, 2023

対応して v0.3.0 をリリースしました。

@dalance dalance closed this as completed Jan 27, 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