From 315b9da3282b29fe16d402bb418e4a2ae002957e Mon Sep 17 00:00:00 2001 From: shadowlink0122 Date: Wed, 11 Mar 2026 00:12:30 +0900 Subject: [PATCH 01/12] =?UTF-8?q?v0.15.1:=20SV=E6=A7=8B=E6=96=87=E6=8B=A1?= =?UTF-8?q?=E5=BC=B5=20-=20=E9=80=A3=E6=8E=A5=E3=83=BB=E8=A4=87=E8=A3=BD?= =?UTF-8?q?=E3=83=BBalways=5F*=E3=83=BBbit[N]=E3=83=BBenum/struct/function?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v0.15.0のPR#14マージ以降の全変更をfeature/v0.15.1に移行: - SVバックエンド品質改善: 定数ビット幅推論・else if正規化・冗長除去 - always_ff/always_comb/always_latch キーワード直接サポートと自動判別 - SV構文拡張Phase1: bit[N]型・assign文・inoutサポート - SV構文拡張Phase2: enum→typedef enum・struct→struct packed・function→function automatic - SV連接({a,b})・複製({N{expr}})構文のフルパイプライン実装 - 全60テストPASS --- VERSION | 2 +- docs/v0.15.1/sv_cm_mapping.md | 103 +++ docs/v0.15.1/sv_extension_proposal.md | 228 +++++++ docs/v0.15.1/sv_language_design.md | 604 ++++++++++++++++++ docs/v0.15.1/sv_syntax_reference.md | 208 ++++++ src/codegen/sv/codegen.cpp | 456 ++++++++++++- src/codegen/sv/codegen.hpp | 7 +- src/frontend/ast/decl.hpp | 5 + src/frontend/ast/types.hpp | 4 + src/frontend/lexer/lexer.cpp | 7 + src/frontend/lexer/token.cpp | 14 + src/frontend/lexer/token.hpp | 7 + src/frontend/parser/parser_decl.cpp | 82 ++- src/frontend/parser/parser_expr.cpp | 113 +++- src/frontend/parser/parser_stmt.cpp | 1 + src/frontend/parser/parser_type.cpp | 4 + src/frontend/types/checking/call.cpp | 16 + src/hir/lowering/decl.cpp | 10 + src/hir/lowering/expr.cpp | 6 +- src/hir/nodes.hpp | 5 +- src/main.cpp | 2 +- src/mir/lowering/base.cpp | 4 +- src/mir/lowering/impl.cpp | 5 + src/mir/nodes.hpp | 4 + tests/sv/advanced/always_async_reset.cm | 16 + tests/sv/advanced/always_async_reset.expect | 1 + tests/sv/advanced/always_auto_latch.cm | 13 + tests/sv/advanced/always_auto_latch.expect | 1 + tests/sv/advanced/always_comb_explicit.cm | 13 + tests/sv/advanced/always_comb_explicit.expect | 1 + tests/sv/advanced/always_comb_mux.cm | 16 + tests/sv/advanced/always_comb_mux.expect | 1 + tests/sv/advanced/always_counter.cm | 15 + tests/sv/advanced/always_counter.expect | 1 + tests/sv/advanced/always_ff_explicit.cm | 11 + tests/sv/advanced/always_ff_explicit.expect | 1 + tests/sv/advanced/backward_compat_async.cm | 22 + .../sv/advanced/backward_compat_async.expect | 1 + tests/sv/advanced/backward_compat_comb.cm | 16 + tests/sv/advanced/backward_compat_comb.expect | 1 + tests/sv/advanced/backward_compat_posedge.cm | 16 + .../advanced/backward_compat_posedge.expect | 1 + tests/sv/advanced/clock_domain.cm | 16 + tests/sv/advanced/clock_domain.expect | 1 + tests/sv/advanced/concat_replicate.cm | 13 + tests/sv/advanced/concat_replicate.expect | 1 + tests/sv/advanced/const_expr.cm | 21 + tests/sv/advanced/const_expr.expect | 1 + tests/sv/advanced/enum_typedef.cm | 23 + tests/sv/advanced/enum_typedef.expect | 1 + tests/sv/advanced/latch_explicit.cm | 13 + tests/sv/advanced/latch_explicit.expect | 1 + tests/sv/advanced/localparam_const.cm | 23 + tests/sv/advanced/localparam_const.expect | 1 + tests/sv/advanced/mixed_always.cm | 28 + tests/sv/advanced/mixed_always.expect | 1 + tests/sv/advanced/multi_always_comb.cm | 18 + tests/sv/advanced/multi_always_comb.expect | 1 + tests/sv/advanced/struct_packed.cm | 16 + tests/sv/advanced/struct_packed.expect | 1 + tests/sv/advanced/sv_function.cm | 19 + tests/sv/advanced/sv_function.expect | 1 + tests/sv/advanced/sv_param.cm | 18 + tests/sv/advanced/sv_param.expect | 1 + tests/sv/advanced/uart_counter.cm | 44 ++ tests/sv/advanced/uart_counter.expect | 1 + tests/sv/basic/all_comparisons.cm | 21 + tests/sv/basic/all_comparisons.expect | 1 + tests/sv/basic/all_operators.cm | 27 + tests/sv/basic/all_operators.expect | 1 + tests/sv/basic/assign_wire.cm | 9 + tests/sv/basic/assign_wire.expect | 1 + tests/sv/basic/bit_width.cm | 21 + tests/sv/basic/bit_width.expect | 1 + tests/sv/basic/bool_logic.cm | 15 + tests/sv/basic/bool_logic.expect | 1 + tests/sv/basic/increment.cm | 10 + tests/sv/basic/increment.expect | 1 + tests/sv/basic/inout_port.cm | 16 + tests/sv/basic/inout_port.expect | 1 + tests/sv/basic/internal_reg.cm | 23 + tests/sv/basic/internal_reg.expect | 1 + tests/sv/basic/nested_ternary.cm | 14 + tests/sv/basic/nested_ternary.expect | 1 + tests/sv/basic/signed_types.cm | 19 + tests/sv/basic/signed_types.expect | 1 + tests/sv/basic/unsigned_types.cm | 19 + tests/sv/basic/unsigned_types.expect | 1 + tests/sv/control/compound_conditions.cm | 17 + tests/sv/control/compound_conditions.expect | 1 + tests/sv/control/deep_if_else.cm | 38 ++ tests/sv/control/deep_if_else.expect | 1 + tests/sv/control/for_loop.cm | 15 + tests/sv/control/for_loop.expect | 1 + tests/sv/control/switch_case.cm | 29 + tests/sv/control/switch_case.expect | 1 + tests/sv/control/switch_fsm.cm | 37 ++ tests/sv/control/switch_fsm.expect | 1 + vscode-extension/package.json | 2 +- 99 files changed, 2597 insertions(+), 59 deletions(-) create mode 100644 docs/v0.15.1/sv_cm_mapping.md create mode 100644 docs/v0.15.1/sv_extension_proposal.md create mode 100644 docs/v0.15.1/sv_language_design.md create mode 100644 docs/v0.15.1/sv_syntax_reference.md create mode 100644 tests/sv/advanced/always_async_reset.cm create mode 100644 tests/sv/advanced/always_async_reset.expect create mode 100644 tests/sv/advanced/always_auto_latch.cm create mode 100644 tests/sv/advanced/always_auto_latch.expect create mode 100644 tests/sv/advanced/always_comb_explicit.cm create mode 100644 tests/sv/advanced/always_comb_explicit.expect create mode 100644 tests/sv/advanced/always_comb_mux.cm create mode 100644 tests/sv/advanced/always_comb_mux.expect create mode 100644 tests/sv/advanced/always_counter.cm create mode 100644 tests/sv/advanced/always_counter.expect create mode 100644 tests/sv/advanced/always_ff_explicit.cm create mode 100644 tests/sv/advanced/always_ff_explicit.expect create mode 100644 tests/sv/advanced/backward_compat_async.cm create mode 100644 tests/sv/advanced/backward_compat_async.expect create mode 100644 tests/sv/advanced/backward_compat_comb.cm create mode 100644 tests/sv/advanced/backward_compat_comb.expect create mode 100644 tests/sv/advanced/backward_compat_posedge.cm create mode 100644 tests/sv/advanced/backward_compat_posedge.expect create mode 100644 tests/sv/advanced/clock_domain.cm create mode 100644 tests/sv/advanced/clock_domain.expect create mode 100644 tests/sv/advanced/concat_replicate.cm create mode 100644 tests/sv/advanced/concat_replicate.expect create mode 100644 tests/sv/advanced/const_expr.cm create mode 100644 tests/sv/advanced/const_expr.expect create mode 100644 tests/sv/advanced/enum_typedef.cm create mode 100644 tests/sv/advanced/enum_typedef.expect create mode 100644 tests/sv/advanced/latch_explicit.cm create mode 100644 tests/sv/advanced/latch_explicit.expect create mode 100644 tests/sv/advanced/localparam_const.cm create mode 100644 tests/sv/advanced/localparam_const.expect create mode 100644 tests/sv/advanced/mixed_always.cm create mode 100644 tests/sv/advanced/mixed_always.expect create mode 100644 tests/sv/advanced/multi_always_comb.cm create mode 100644 tests/sv/advanced/multi_always_comb.expect create mode 100644 tests/sv/advanced/struct_packed.cm create mode 100644 tests/sv/advanced/struct_packed.expect create mode 100644 tests/sv/advanced/sv_function.cm create mode 100644 tests/sv/advanced/sv_function.expect create mode 100644 tests/sv/advanced/sv_param.cm create mode 100644 tests/sv/advanced/sv_param.expect create mode 100644 tests/sv/advanced/uart_counter.cm create mode 100644 tests/sv/advanced/uart_counter.expect create mode 100644 tests/sv/basic/all_comparisons.cm create mode 100644 tests/sv/basic/all_comparisons.expect create mode 100644 tests/sv/basic/all_operators.cm create mode 100644 tests/sv/basic/all_operators.expect create mode 100644 tests/sv/basic/assign_wire.cm create mode 100644 tests/sv/basic/assign_wire.expect create mode 100644 tests/sv/basic/bit_width.cm create mode 100644 tests/sv/basic/bit_width.expect create mode 100644 tests/sv/basic/bool_logic.cm create mode 100644 tests/sv/basic/bool_logic.expect create mode 100644 tests/sv/basic/increment.cm create mode 100644 tests/sv/basic/increment.expect create mode 100644 tests/sv/basic/inout_port.cm create mode 100644 tests/sv/basic/inout_port.expect create mode 100644 tests/sv/basic/internal_reg.cm create mode 100644 tests/sv/basic/internal_reg.expect create mode 100644 tests/sv/basic/nested_ternary.cm create mode 100644 tests/sv/basic/nested_ternary.expect create mode 100644 tests/sv/basic/signed_types.cm create mode 100644 tests/sv/basic/signed_types.expect create mode 100644 tests/sv/basic/unsigned_types.cm create mode 100644 tests/sv/basic/unsigned_types.expect create mode 100644 tests/sv/control/compound_conditions.cm create mode 100644 tests/sv/control/compound_conditions.expect create mode 100644 tests/sv/control/deep_if_else.cm create mode 100644 tests/sv/control/deep_if_else.expect create mode 100644 tests/sv/control/for_loop.cm create mode 100644 tests/sv/control/for_loop.expect create mode 100644 tests/sv/control/switch_case.cm create mode 100644 tests/sv/control/switch_case.expect create mode 100644 tests/sv/control/switch_fsm.cm create mode 100644 tests/sv/control/switch_fsm.expect diff --git a/VERSION b/VERSION index a5510516..e815b861 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.15.0 +0.15.1 diff --git a/docs/v0.15.1/sv_cm_mapping.md b/docs/v0.15.1/sv_cm_mapping.md new file mode 100644 index 00000000..a8bfc0db --- /dev/null +++ b/docs/v0.15.1/sv_cm_mapping.md @@ -0,0 +1,103 @@ +# Cm ⇔ SystemVerilog マッピング対応表 + +Cmの構文要素がSVバックエンドでどのように変換されるかの完全な対応表。 + +--- + +## 1. 関数 → ブロック マッピング + +| Cm構文 | `is_async` | トリガパラメータ | SV出力 | 代入方式 | +|-------|------------|----------------|--------|---------| +| `void f(posedge clk) {...}` | N/A | `posedge clk` | `always_ff @(posedge clk)` | `<=` | +| `void f(negedge rst) {...}` | N/A | `negedge rst` | `always_ff @(negedge rst)` | `<=` | +| `async func f() {...}` | `true` | なし | `always_ff @(posedge clk)` | `<=` | +| `void f() {...}` | `false` | なし | `always_comb` | `=` | +| `func f() {...}` | `false` | なし | `always_comb` | `=` | + +> [!IMPORTANT] +> **`async` キーワードの二重意味**: `async` は元々 JavaScript バックエンド用の非同期関数マーカー。 +> SV バックエンドではこれを `always_ff` 生成のトリガとして流用している。 +> MIR の `is_async` フラグが両バックエンドで異なる意味を持つ。 + +--- + +## 2. 変数宣言マッピング + +| Cm宣言 | 属性 | SV出力 | +|-------|------|--------| +| `#[input] posedge clk;` | `input` | `input logic clk` (ポート) | +| `#[input] bool rst = false;` | `input` | `input logic rst` (ポート) | +| `#[output] utiny led = 0xFF;` | `output` | `output logic [7:0] led` (ポート) | +| `#[inout] uint data;` | `inout` | `inout logic [31:0] data` (ポート) | +| `#[sv::param] uint WIDTH = 8;` | `sv::param` | `parameter WIDTH = 32'd8;` | +| `uint counter = 0;` | なし | `logic [31:0] counter;` (内部レジスタ) | + +--- + +## 3. SV構文のうちCmに対応がないもの + +以下のSV構文は、現在のCmバックエンドでは**生成されない**: + +### 3.1 生成されないSVブロック + +| SV構文 | 説明 | 現状 | +|--------|------|------| +| `function ... endfunction` | 組み合わせロジック関数 | Cm `func` → `always_comb` に変換 | +| `task ... endtask` | 手続き的タスク | 未サポート | +| `initial begin ... end` | シミュレーション初期化 | テストベンチのみ | +| `generate ... endgenerate` | パラメトリック生成 | 未サポート | +| `always @(*)` | 旧来の組み合わせ | `always_comb` を使用 | +| `always @(posedge ... or negedge ...)` | 非同期リセット | 未サポート | +| `assign wire = expr;` | 連続代入 | 未サポート | + +### 3.2 生成されないSVデータ型 + +| SV構文 | 説明 | 現状 | +|--------|------|------| +| `integer` | 32-bit符号付き (旧) | `logic signed [31:0]` を使用 | +| `real` | 浮動小数点 | 非合成 → エラー | +| `bit` | 2-state (0/1のみ) | `logic` (4-state) を使用 | +| `byte` | 8-bit符号付き | `logic signed [7:0]` を使用 | +| `shortint` | 16-bit符号付き | `logic signed [15:0]` を使用 | +| `longint` | 64-bit符号付き | `logic signed [63:0]` を使用 | +| `struct packed {...}` | パックド構造体 | 未サポート | +| `enum {...}` | 列挙型 | 未サポート | +| `typedef` | 型エイリアス | 未サポート | + +### 3.3 生成されないSV演算子/構文 + +| SV構文 | 説明 | 現状 | +|--------|------|------| +| `{a, b}` | 連接 (concatenation) | 未サポート | +| `{N{expr}}` | 複製 (replication) | 未サポート | +| `a ? b : c` | 三項演算子 | MIRのSwitchIntで分岐化 | +| `$clog2(N)` | システム関数 | 未サポート | +| `for (;;)` | forループ | 未サポート (静的展開のみ) | +| `localparam` | ローカルパラメータ | `parameter` のみ | + +--- + +## 4. Cmキーワードの SV バックエンドでの意味変化 + +| Cmキーワード | 通常(LLVM)の意味 | SVバックエンドの意味 | +|-------------|-----------------|-------------------| +| `async` | JS非同期関数 | `always_ff` ブロック生成 | +| `func` | 関数宣言 (戻り値推論) | `always_comb` ブロック生成 | +| `void` | 戻り値なし関数 | ブロック生成 (ff/comb) | +| `=` | 変数代入 | ff内: `<=`, comb内: `=` | +| `!` | 論理否定 | `~` (ビット反転に統合) | +| `struct` | 構造体定義 | **未サポート** | +| `enum` | 列挙型定義 | **未サポート** | +| `for` | ループ | **未サポート** (将来: generate for?) | +| `match` | パターンマッチ | `case` 文に変換 | + +--- + +## 5. 暗黙の動作 + +| 動作 | 条件 | 説明 | +|------|------|------| +| `clk` ポート自動追加 | `async func` 存在 & `clk` 未宣言 | `input logic clk` を先頭に追加 | +| `rst` ポート自動追加 | `async func` 存在 & `rst` 未宣言 | `input logic rst` を `clk` の後に追加 | +| 一時変数インライン展開 | `_tXXXX` 変数 | MIRテンポラリを式に展開 | +| `self.` プレフィックス除去 | `self.xxx` | SVでは `xxx` に短縮 | diff --git a/docs/v0.15.1/sv_extension_proposal.md b/docs/v0.15.1/sv_extension_proposal.md new file mode 100644 index 00000000..ecb5bb2f --- /dev/null +++ b/docs/v0.15.1/sv_extension_proposal.md @@ -0,0 +1,228 @@ +# SV バックエンド 構文拡張提案 (v0.15.1) + +## 背景 + +現在の Cm SV バックエンドは、Cm の汎用構文(`async`, `func`, `void`)を +SV の `always_ff` / `always_comb` にマッピングしている。 +しかし、SV には Cm に直接対応する構文がない機能が多数あり、 +また Cm のキーワードが SW/HW で異なる意味を持つ問題がある。 + +本ドキュメントでは、ユーザーの提案を含む構文拡張の候補を列挙する。 + +--- + +## 拡張1: `always_ff` マッピングの明示化 + +### 現状の問題 + +```cm +// 方法A: asyncキーワード流用 — JSバックエンドと意味が衝突 +async func tick() { ... } // → always_ff @(posedge clk) + +// 方法B: posedgeパラメータ — 意味は明確だが構文が特殊 +void blink(posedge clk) { ... } // → always_ff @(posedge clk) +``` + +`async` は JS の非同期と意味が衝突し、SV ユーザーには直感的でない。 + +### 提案: `async void ff(...)` 構文 + +```cm +// 提案: async と void を組み合わせた明示的な構文 +async void ff() { ... } // → always_ff @(posedge clk) +async void ff(posedge clk) { ... } // → always_ff @(posedge clk) +async void ff(negedge rst) { ... } // → always_ff @(negedge rst) +``` + +#### メリット +- `async` = 順序回路 (クロック同期) を明示 +- `void ff()` = 「flip-flop ブロック」と自然に読める +- 既存の `async func` との後方互換性を維持可能 + +#### 検討事項 +- `ff` は関数名か予約語か? → **関数名** として扱い、命名規則で意味付与 +- `async void` と `async func` の共存ルールが必要 + +--- + +## 拡張2: `function` / `task` の SV ネイティブ対応 + +### 現状の問題 + +```cm +func select() { ... } // → always_comb — SV の function とは異なる +``` + +SV の `function` は **純粋な組み合わせ論理関数** で、 +モジュール内で呼び出し可能な再利用可能なロジック。 +Cm の `func` はこれとは異なり `always_comb` ブロック全体を生成する。 + +### 提案 + +| 新Cm構文 | SV出力 | 用途 | +|---------|--------|------| +| `#[sv::function] func f(uint a, uint b) -> uint {...}` | `function ... endfunction` | 再利用可能な組み合わせロジック | +| `#[sv::task] void f() {...}` | `task ... endtask` | 手続き的ロジック | + +あるいは: +```cm +// SV function を直接記述 +sv function uint mux(uint a, uint b, bool sel) { + return sel ? a : b; +} +``` + +--- + +## 拡張3: `assign` (連続代入) のサポート + +### 現状 +ワイヤへの連続代入 (`assign`) は未サポート。 + +### 提案 +```cm +// 方法A: wire型 + 初期値で推論 +#[output] wire led = (counter > 25000000); +// → assign led = (counter > 25000000); + +// 方法B: 属性で明示 +#[sv::assign] +bool led = (counter > 25000000); +// → assign led = (counter > 25000000); +``` + +--- + +## 拡張4: `generate for` / パラメトリック生成 + +### 現状 +ループの SV 出力は未サポート。 + +### 提案 +```cm +// 定数ループ → generate for +#[sv::generate] +for (uint i = 0; i < WIDTH; i++) { + assign out[i] = in[WIDTH - 1 - i]; +} +// → genvar i; +// → generate for (i = 0; i < WIDTH; i = i + 1) begin +// → assign out[i] = in[WIDTH - 1 - i]; +// → end endgenerate +``` + +--- + +## 拡張5: 連接 / ビットスライス演算子 + +### 現状 +SV の `{a, b}` (連接) や `a[3:0]` (ビットスライス) は未サポート。 + +### 提案 +```cm +// 連接: 新演算子 or 関数 +uint result = {a, b}; // 方法A: SV構文リテラル +uint result = concat(a, b); // 方法B: ビルトイン関数 + +// ビットスライス: 配列添字の拡張 +utiny low = data[7:0]; // 方法A: 範囲添字 +utiny low = data.bits(7, 0); // 方法B: メソッド +``` + +--- + +## 拡張6: 非同期リセット対応 + +### 現状 +`always_ff @(posedge clk or negedge rst_n)` は生成できない。 + +### 提案 +```cm +// 複数エッジの指定 +void process(posedge clk, negedge rst_n) { + if (!rst_n) { + counter = 0; + } else { + counter = counter + 1; + } +} +// → always_ff @(posedge clk or negedge rst_n) begin +// if (!rst_n) begin +// counter <= 0; +// end else begin +// counter <= counter + 1; +// end +// end +``` + +--- + +## 拡張7: `localparam` のサポート + +### 現状 +`parameter` はポートレベル。ローカル定数は `localparam` にすべき。 + +### 提案 +```cm +// const + 属性なし → localparam +const uint CLK_FREQ = 50_000_000; +// → localparam CLK_FREQ = 32'd50000000; + +// #[sv::param] 付き → parameter (外部から変更可能) +#[sv::param] const uint WIDTH = 8; +// → parameter WIDTH = 32'd8; +``` + +--- + +## 拡張8: `struct packed` / `enum` のサポート + +### 現状 +Cm の `struct` / `enum` は SV バックエンドで未サポート。 + +### 提案 +```cm +//! platform: sv + +// パックド構造体 +#[sv::packed] +struct AXIAddr { + uint addr; + utiny len; + utiny size; + utiny burst; +} +// → typedef struct packed { +// logic [31:0] addr; +// logic [7:0] len; +// logic [7:0] size; +// logic [7:0] burst; +// } AXIAddr; + +// 列挙型 (FSM状態) +#[sv::enum] +enum State { + IDLE, + READ, + WRITE, + DONE +} +// → typedef enum logic [1:0] { +// IDLE = 2'd0, READ = 2'd1, WRITE = 2'd2, DONE = 2'd3 +// } State; +``` + +--- + +## 優先度まとめ + +| 優先度 | 拡張 | 理由 | +|-------|------|------| +| **P0** | 拡張1: always_ff明示化 | 既存 `async` の意味衝突を解消 | +| **P0** | 拡張6: 非同期リセット | 実用的なFPGA設計に必須 | +| **P0** | 拡張7: localparam | `const` → `localparam` は自然 | +| **P1** | 拡張3: assign | ワイヤの連続代入は頻出パターン | +| **P1** | 拡張5: 連接/スライス | ビット操作はHDLの基本 | +| **P2** | 拡張2: function/task | 再利用ロジックの定義 | +| **P2** | 拡張8: struct/enum | FSM設計パターンに必要 | +| **P3** | 拡張4: generate | パラメトリック設計 | diff --git a/docs/v0.15.1/sv_language_design.md b/docs/v0.15.1/sv_language_design.md new file mode 100644 index 00000000..d98ac056 --- /dev/null +++ b/docs/v0.15.1/sv_language_design.md @@ -0,0 +1,604 @@ +# Cm SV バックエンド 言語デザイン v0.15.1 + +> **設計原則**: Cmの既存構文を最大限活かし、SV固有の概念のみ新トークンで追加する。 + +--- + +## 新規トークン (追加) + +| トークン | キーワード | 用途 | +|---------|---------|------| +| `KwAlways` | `always` | SV ロジックブロック修飾子 | +| `KwAssign` | `assign` | 連続代入文 | +| `KwInitial` | `initial` | シミュレーション初期化ブロック | +| `KwBit` | `bit` | 任意ビット幅型 `bit` | + +※ 既存の `KwPosedge`, `KwNegedge`, `KwWire`, `KwReg` はそのまま維持。 + +--- + +## 1. コンパイルモデル + +``` +cm compile --target=sv input.cm -o output.sv +``` + +**1ファイル = 1モジュール** の原則: + +| 項目 | Cm (LLVM) | Cm (SV) | +|------|-----------|---------| +| `import` の動作 | 再帰的にフラット化 → 1バイナリ | **モジュール参照のみ** → 別ファイル | +| 出力 | 1つの実行ファイル | **1つの .sv ファイル** | +| リンク | コンパイラが行う | **Gowin EDA / Yosys** が行う | + +```cm +//! platform: sv +import Gowin_OSC; // ← Gowin_OSCモジュールの「存在」を知る(コンパイルはしない) +import UART_TX; // ← UART_TXモジュールの「存在」を知る(コンパイルはしない) +``` + +ファイル名からモジュール名を自動推定。`//! platform: sv` 指定必須。 + +--- + +## 2. ポート宣言 (変更なし) + +```cm +#[input] posedge clk; +#[input] negedge rst_n; +#[input] bool enable = false; +#[output] utiny led = 0xFF; +#[output] uint data_out; +#[inout] ushort bus; +``` + +既存の `#[input]`/`#[output]`/`#[inout]` 属性をそのまま使用。 + +--- + +## 3. ロジックブロック + +### 3.1 always_ff (順序回路) + +`always` + エッジパラメータ → `always_ff @(...)` を生成。 + +```cm +// 基本: posedge clk +always void counter(posedge clk) { + count = count + 1; +} +// → always_ff @(posedge clk) begin +// count <= count + 32'd1; +// end + +// 非同期リセット: 複数エッジ +always void process(posedge clk, negedge rst_n) { + if (!rst_n) { + count = 0; + } else { + count = count + 1; + } +} +// → always_ff @(posedge clk or negedge rst_n) begin +// if (!rst_n) begin +// count <= 32'd0; +// end else begin +// count <= count + 32'd1; +// end +// end +``` + +**代入ルール**: `always` ブロック内の `=` は自動的に `<=` (ノンブロッキング) にマッピング。 + +### 3.2 always_comb (組み合わせ回路) + +`always` + エッジパラメータなし → `always_comb` を生成。 + +```cm +always void decode() { + out = 0; // デフォルト値(ラッチ防止) + if (sel) { + out = a; + } else { + out = b; + } +} +// → always_comb begin +// out = 32'd0; +// if (sel) begin +// out = a; +// end else begin +// out = b; +// end +// end +``` + +**代入ルール**: エッジなし `always` ブロック内の `=` はブロッキング代入 (`=`) のまま。 + +### 3.3 後方互換 + +```cm +// 旧構文A: async → always_ff @(posedge clk) として引き続き動作 +async void tick() { + count = count + 1; +} + +// 旧構文B: posedgeパラメータ → always_ff として引き続き動作 +void blink(posedge clk) { + led = !led; +} + +// 旧構文C: トリガなし void → always_comb として引き続き動作 +void update() { + signal = (counter > 100); +} +``` + +--- + +## 4. 連続代入 (assign) + +```cm +// assign文: wire的な組み合わせ出力 +assign bool led = (counter > 25000000); +// → assign led = (counter > 25000000); + +assign utiny mux_out = sel ? a : b; +// → assign mux_out = sel ? a : b; +``` + +`assign` 変数は自動的にポートリストまたはwire宣言に反映。 + +--- + +## 5. 定数パラメータ + +```cm +// const → localparam (モジュール内ローカル定数) +const uint CLK_FREQ = 50_000_000; +const uint CNT_MAX = CLK_FREQ / 2 - 1; +// → localparam CLK_FREQ = 32'd50000000; +// → localparam CNT_MAX = CLK_FREQ / 2 - 32'd1; + +// #[sv::param] → parameter (外部から上書き可能) +#[sv::param] const uint WIDTH = 8; +// → parameter WIDTH = 32'd8; +``` + +--- + +## 6. 型システム + +### 6.1 基本型 (変更なし) + +| Cm型 | SV出力 | 幅 | +|------|--------|-----| +| `bool` | `logic` | 1 | +| `utiny` | `logic [7:0]` | 8 | +| `ushort` | `logic [15:0]` | 16 | +| `uint` | `logic [31:0]` | 32 | +| `ulong` | `logic [63:0]` | 64 | +| `tiny` | `logic signed [7:0]` | 8 | +| `short` | `logic signed [15:0]` | 16 | +| `int` | `logic signed [31:0]` | 32 | +| `long` | `logic signed [63:0]` | 64 | + +### 6.2 SV固有型 (変更なし) + +| Cm型 | SV用途 | +|------|--------| +| `posedge` | クロック立ち上がり | +| `negedge` | クロック/リセット立ち下がり | +| `wire` | ワイヤ修飾 | +| `reg` | レジスタ修飾 | + +### 6.3 カスタムビット幅 (新規) + +```cm +// 任意ビット幅: bit 構文 +#[output] bit<4> nibble; // → output logic [3:0] nibble +#[output] bit<12> address; // → output logic [11:0] address + +bit<26> counter; // → logic [25:0] counter +``` + +> [!NOTE] +> `bit` は **新規型** として追加。SV の合成設計で頻出する任意ビット幅をサポート。 + +--- + +## 7. 演算子 + +### 7.1 既存演算子 (変更なし) + +算術: `+` `-` `*` `/` `%` +ビット: `&` `|` `^` `~` `<<` `>>` +比較: `==` `!=` `<` `<=` `>` `>=` +論理: `&&` `||` `!` + +### 7.2 新規演算子・ビルトイン + +| Cm構文 | SV出力 | 用途 | +|-------|--------|------| +| `{a, b}` | `{a, b}` | 連接 (concatenation) | +| `{a, b, c}` | `{a, b, c}` | 多項連接 | +| `{N{expr}}` | `{N{expr}}` | 複製 (replication) | +| `x[7:0]` | `x[7:0]` | ビットスライス | +| `x[i]` | `x[i]` | ビット選択 | +| `!x` | `!x` | 論理否定 (1-bit) | +| `~x` | `~x` | ビット反転 | + +> [!NOTE] +> **連接 `{a, b}`**: 式コンテキスト(代入RHS、関数引数等)では連接式、 +> 制御構文の直後ではブロック `{...}` として、パーサーが意味論的に区別する。 +> 代替として `concat(a, b)` ビルトイン関数も利用可能。 +> **インクリメント**: `count++` は `count = count + 1` に展開される。 + +### 7.3 三項演算子 + +```cm +assign uint result = (sel) ? a : b; +// → assign result = (sel) ? a : b; +``` + +Cm の三項演算子 `?:` をそのまま SV の三項にマッピング。 + +--- + +## 8. 制御構文 + +### 8.1 if/else (変更なし) + +```cm +if (condition) { + // ... +} else if (other) { + // ... +} else { + // ... +} +``` + +### 8.2 switch → case + +```cm +switch (state) { + case 0: { + next_state = 1; + } + case 1: { + next_state = 2; + } + default: { + next_state = 0; + } +} +// → case (state) +// 32'd0: begin next_state <= 32'd1; end +// 32'd1: begin next_state <= 32'd2; end +// default: begin next_state <= 32'd0; end +// endcase +``` + +### 8.3 for ループ (新規: generate対応) + +```cm +// 静的forループ → generate for +for (uint i = 0; i < WIDTH; i = i + 1) { + assign out[i] = in[WIDTH - 1 - i]; +} +// → genvar i; +// → generate for (i = 0; i < WIDTH; i = i + 1) begin : gen_reverse +// assign out[i] = in[WIDTH - 1 - i]; +// end endgenerate +``` + +--- + +## 9. 構造化型 + +### 9.1 パックド構造体 + +```cm +#[sv::packed] +struct AXIAddr { + uint addr; + utiny len; + utiny size; + utiny burst; +} +// → typedef struct packed { +// logic [31:0] addr; +// logic [7:0] len; +// logic [7:0] size; +// logic [7:0] burst; +// } AXIAddr; +``` + +### 9.2 FSM用列挙型 + +```cm +enum State { + IDLE, + READ, + WRITE, + DONE +} +// → typedef enum logic [1:0] { +// IDLE = 2'd0, +// READ = 2'd1, +// WRITE = 2'd2, +// DONE = 2'd3 +// } State; +``` + +Cmの既存 `enum` 構文を SV の `typedef enum` にマッピング。 +ビット幅はバリアント数から自動計算。 + +--- + +## 10. SV function / task + +### 10.1 function (純粋組み合わせ関数) + +```cm +// #[sv::function] 属性 → SV function +#[sv::function] +uint mux4(uint a, uint b, uint c, uint d, utiny sel) { + switch (sel) { + case 0: { return a; } + case 1: { return b; } + case 2: { return c; } + default: { return d; } + } +} +// → function automatic logic [31:0] mux4( +// input logic [31:0] a, b, c, d, +// input logic [7:0] sel +// ); +// case (sel) +// 8'd0: mux4 = a; +// ... +// endcase +// endfunction +``` + +### 10.2 task (手続き的ブロック) + +```cm +#[sv::task] +void send_byte(utiny data) { + tx_valid = true; + tx_data = data; +} +// → task automatic send_byte(input logic [7:0] data); +// tx_valid <= 1'b1; +// tx_data <= data; +// endtask +``` + +--- + +## 11. メモリ推論 + +```cm +#[sv::bram] +utiny memory[1024]; // → (* ram_style = "block" *) logic [7:0] memory [0:1023]; + +#[sv::lutram] +utiny lookup_table[16]; // → (* ram_style = "distributed" *) logic [7:0] lookup_table [0:15]; +``` + +--- + +## 12. モジュールインスタンス化 (import/export) + +```cm +// 外部モジュールのインポート +import Gowin_OSC; + +// インスタンス化(名前付き接続) +Gowin_OSC osc_inst( + .oscout = clk +); +// → Gowin_OSC osc_inst ( +// .oscout(clk) +// ); + +// 複数モジュールのインポート +import UART_TX; +import UART_RX; + +UART_TX tx_inst(.clk = clk, .data = tx_data, .tx = tx_pin); +UART_RX rx_inst(.clk = clk, .rx = rx_pin, .data = rx_data); +``` + +自分のモジュールを外部公開する場合: +```cm +//! platform: sv +export; // このモジュールを他のCmファイルからimport可能にする + +#[input] posedge clk; +#[output] bool tx; +// ... +``` + +--- + +## 13. initial ブロック (シミュレーション専用) + +```cm +initial { + clk = false; + rst = true; + // 10ns後にリセット解除 + rst = false; +} +// → initial begin +// clk = 1'b0; +// rst = 1'b1; +// #10 rst = 1'b0; +// end +``` + +--- + +## 14. 定数リテラル (変更なし) + +| Cm | SV出力 | +|----|--------| +| `true` / `false` | `1'b1` / `1'b0` | +| `42` | `32'd42` (コンテキスト依存) | +| `8'b10101010` | `8'b10101010` | +| `16'hFF00` | `16'hFF00` | + +--- + +## 15. 属性一覧 + +| 属性 | SV効果 | カテゴリ | +|------|--------|---------| +| `#[input]` | 入力ポート | ポート | +| `#[output]` | 出力ポート | ポート | +| `#[inout]` | 双方向ポート | ポート | +| `#[sv::param]` | `parameter` | パラメータ | +| `#[sv::bram]` | `(* ram_style = "block" *)` | メモリ | +| `#[sv::lutram]` | `(* ram_style = "distributed" *)` | メモリ | +| `#[sv::clock_domain("name")]` | クロック指定 | タイミング | +| `#[sv::pipeline]` | パイプラインヒント | 合成 | +| `#[sv::share]` | リソース共有 | 合成 | +| `#[sv::packed]` | パックド構造体 | 型 | +| `#[sv::function]` | SV function生成 | ブロック | +| `#[sv::task]` | SV task生成 | ブロック | +| `#[sv::module]` | 外部モジュール宣言 | インスタンス | +| `#[sv::pin("XX")]` | ピン割当 | 物理 | +| `#[sv::iostandard("YY")]` | IO標準 | 物理 | + +--- + +## 16. 完全な回路例 + +```cm +//! platform: sv + +// ポート宣言 +#[input] posedge clk; +#[input] negedge rst_n; +#[output] bool led; + +// 定数 +const uint CLK_FREQ = 50_000_000; +const uint CNT_MAX = CLK_FREQ / 2 - 1; + +// 内部レジスタ +uint counter = 0; + +// FSM状態 +enum State { IDLE, RUN, DONE } +State state = State::IDLE; + +// 順序回路(非同期リセット付き) +always void process(posedge clk, negedge rst_n) { + if (!rst_n) { + counter = 0; + led = false; + state = State::IDLE; + } else { + switch (state) { + case State::IDLE: { + state = State::RUN; + } + case State::RUN: { + if (counter == CNT_MAX) { + counter = 0; + led = !led; + } else { + counter = counter + 1; + } + } + default: {} + } + } +} +``` + +出力SV: +```systemverilog +module example ( + input logic clk, + input logic rst_n, + output logic led +); + localparam CLK_FREQ = 32'd50000000; + localparam CNT_MAX = CLK_FREQ / 2 - 32'd1; + + logic [31:0] counter; + + typedef enum logic [1:0] { + IDLE = 2'd0, RUN = 2'd1, DONE = 2'd2 + } State; + State state; + + always_ff @(posedge clk or negedge rst_n) begin + if (!rst_n) begin + counter <= 32'd0; + led <= 1'b0; + state <= IDLE; + end else begin + case (state) + IDLE: begin + state <= RUN; + end + RUN: begin + if (counter == CNT_MAX) begin + counter <= 32'd0; + led <= ~led; + end else begin + counter <= counter + 32'd1; + end + end + default: begin end + endcase + end + end +endmodule +``` + +--- + +## トークン一覧 (最終) + +### 既存トークン (SV バックエンドで使用) + +| トークン | SV での意味 | +|---------|-----------| +| `KwAsync` | `always_ff` (後方互換) | +| `KwVoid` | ブロック戻り型 | +| `KwConst` | `localparam` | +| `KwStruct` | `struct packed` (+ 属性) | +| `KwEnum` | `typedef enum` | +| `KwSwitch`/`KwCase`/`KwDefault` | `case/endcase` | +| `KwFor` | `generate for` | +| `KwReturn` | `function` 戻り値 | +| `KwIf`/`KwElse` | `if/else` | +| `KwExtern` | 外部モジュール宣言 | +| `KwPosedge` | `posedge` 信号型 | +| `KwNegedge` | `negedge` 信号型 | +| `KwWire` | `wire` 修飾型 | +| `KwReg` | `reg` 修飾型 | + +### 新規トークン + +| トークン | キーワード | SV での意味 | +|---------|---------|-----------| +| `KwAlways` | `always` | ロジックブロック修飾子 | +| `KwAssign` | `assign` | 連続代入文 | +| `KwInitial` | `initial` | シミュレーション初期化 | +| `KwBit` | `bit` | 任意ビット幅型 `bit` | + +### ビルトイン関数 (SV モード) + +| 関数 | SV出力 | 用途 | +|------|--------|------| +| `concat(a, b, ...)` | `{a, b, ...}` | ビット連接 | +| `replicate(expr, N)` | `{N{expr}}` | ビット複製 | diff --git a/docs/v0.15.1/sv_syntax_reference.md b/docs/v0.15.1/sv_syntax_reference.md new file mode 100644 index 00000000..e7cd0121 --- /dev/null +++ b/docs/v0.15.1/sv_syntax_reference.md @@ -0,0 +1,208 @@ +# SystemVerilog バックエンド 構文・トークン リファレンス + +本ドキュメントは、Cmコンパイラの SV バックエンド (`codegen/sv/codegen.cpp`) が +**出力する全SV構文** と、それに対応する **Cmトークン/型** を網羅的に列挙する。 + +--- + +## 1. モジュール構造体 + +### 出力される SV 構文 + +| SV構文 | 生成元 | 例 | +|--------|--------|-----| +| `module (...)` | ソースファイル名 | `module blink (...)` | +| `endmodule` | 自動 | | +| `` `timescale 1ns / 1ps `` | ファイルヘッダ | | +| `input logic [N:0] ` | `#[input]` 属性 | `input logic clk` | +| `output logic [N:0] ` | `#[output]` 属性 | `output logic [7:0] led` | +| `inout logic [N:0] ` | `#[inout]` 属性 | `inout logic [15:0] data` | +| `parameter = ;` | `#[sv::param]` 属性 | `parameter WIDTH = 8;` | + +--- + +## 2. 型マッピング + +| Cm型 | TypeKind | SV出力 | ビット幅 | +|------|----------|--------|---------| +| `bool` | `Bool` | `logic` | 1 | +| `tiny` | `Tiny` | `logic signed [7:0]` | 8 | +| `utiny` | `UTiny` | `logic [7:0]` | 8 | +| `short` | `Short` | `logic signed [15:0]` | 16 | +| `ushort` | `UShort` | `logic [15:0]` | 16 | +| `int` | `Int` | `logic signed [31:0]` | 32 | +| `uint` | `UInt` | `logic [31:0]` | 32 | +| `long` | `Long` | `logic signed [63:0]` | 64 | +| `ulong` | `ULong` | `logic [63:0]` | 64 | +| `isize` | `ISize` | `logic signed [63:0]` | 64 | +| `usize` | `USize` | `logic [63:0]` | 64 | +| `posedge` | `Posedge` | `logic` (1-bit) | 1 | +| `negedge` | `Negedge` | `logic` (1-bit) | 1 | +| `wire` | `Wire` | `mapType(T)` | T依存 | +| `reg` | `Reg` | `mapType(T)` | T依存 | + +### 非合成型 (SV00x エラー) + +以下の型は SV バックエンドでコンパイルエラーとなる: +- `float`, `double`, `ufloat`, `udouble` — 浮動小数点 +- `string`, `cstring` — 文字列 +- `*T` (Pointer), `&T` (Reference) — ポインタ/参照 + +--- + +## 3. ロジックブロック生成 + +### 3.1 `always_ff` (順序回路) + +| Cmパターン | SV出力 | +|-----------|--------| +| `void f(posedge clk) {...}` | `always_ff @(posedge clk) begin ... end` | +| `void f(negedge rst) {...}` | `always_ff @(negedge rst) begin ... end` | +| `async func f() {...}` | `always_ff @(posedge clk) begin ... end` | +| `#[sv::clock_domain("fast")] async func f() {...}` | `always_ff @(posedge fast) begin ... end` | + +**代入**: ノンブロッキング `<=` + +### 3.2 `always_comb` (組み合わせ回路) + +| Cmパターン | SV出力 | +|-----------|--------| +| `void f() {...}` (トリガなし、非async) | `always_comb begin ... end` | +| `func f() {...}` | `always_comb begin ... end` | + +**代入**: ブロッキング `=` + +### 3.3 `assign` (連続代入) + +現時点では `assign` 文は属性ベースで生成されない。将来のサポート候補。 + +--- + +## 4. 二項演算子マッピング + +| Cm演算子 | MIR Op | SV出力 | +|---------|--------|--------| +| `+` | `Add` | `+` | +| `-` | `Sub` | `-` | +| `*` | `Mul` | `*` | +| `/` | `Div` | `/` | +| `%` | `Mod` | `%` | +| `&` | `BitAnd` | `&` | +| `\|` | `BitOr` | `\|` | +| `^` | `BitXor` | `^` | +| `<<` | `Shl` | `<<` | +| `>>` | `Shr` | `>>` | +| `==` | `Eq` | `==` | +| `!=` | `Ne` | `!=` | +| `<` | `Lt` | `<` | +| `<=` | `Le` | `<=` | +| `>` | `Gt` | `>` | +| `>=` | `Ge` | `>=` | +| `&&` | `And` | `&&` | +| `\|\|` | `Or` | `\|\|` | + +--- + +## 5. 単項演算子マッピング + +| Cm演算子 | MIR Op | SV出力 | +|---------|--------|--------| +| `-x` | `Neg` | `-x` | +| `!x` | `Not` | `~x` | +| `~x` | `BitNot` | `~x` | + +> [!NOTE] +> Cmの `!` (論理否定) と `~` (ビット反転) は、SVでは両方 `~` にマッピングされる。 +> SVの `!` は1ビット論理否定だが、現在のバックエンドは `~` に統一している。 + +--- + +## 6. 定数リテラル + +| Cmリテラル | SV出力例 | +|-----------|---------| +| `true` | `1'b1` | +| `false` | `1'b0` | +| `42` (uint ctx) | `32'd42` | +| `42` (utiny ctx) | `8'd42` | +| `42` (signed int ctx) | `32'sd42` | +| `-5` | `-32'sd5` | +| `8'b10101010` | `8'b10101010` | +| `16'hFF00` | `16'hFF00` | + +--- + +## 7. 制御構文 + +| Cm構文 | SV出力 | +|-------|--------| +| `if (cond) {...}` | `if (cond) begin ... end` | +| `if (cond) {...} else {...}` | `if (cond) begin ... end else begin ... end` | +| `if ... else if ...` | `if ... end else if ...` (正規化) | +| `switch (val) { case X: ... }` | `case (val) X: begin ... end endcase` | + +--- + +## 8. 宣言構文 + +| SV出力 | 生成条件 | +|--------|---------| +| `logic [N:0] ;` | 内部レジスタ (属性なしグローバル変数 / 関数ローカル変数) | +| `(* ram_style = "block" *)` | `#[sv::bram]` 属性 | +| `(* ram_style = "distributed" *)` | `#[sv::lutram]` 属性 | + +--- + +## 9. SV固有トークン (token.hpp) + +| トークン | キーワード | TypeKind | 用途 | +|---------|---------|----------|------| +| `KwPosedge` | `posedge` | `Posedge` | 立ち上がりエッジクロック | +| `KwNegedge` | `negedge` | `Negedge` | 立ち下がりエッジクロック | +| `KwWire` | `wire` | `Wire` | ワイヤ修飾型 | +| `KwReg` | `reg` | `Reg` | レジスタ修飾型 | + +--- + +## 10. SV属性 (Attribute) + +| Cm属性 | SV効果 | +|-------|--------| +| `#[input]` | 入力ポート宣言 | +| `#[output]` | 出力ポート宣言 | +| `#[inout]` | 双方向ポート宣言 | +| `#[sv::param]` | parameter宣言 | +| `#[sv::bram]` | `(* ram_style = "block" *)` | +| `#[sv::lutram]` | `(* ram_style = "distributed" *)` | +| `#[sv::pipeline]` | 合成コメント出力 | +| `#[sv::share]` | リソース共有コメント | +| `#[sv::clock_domain("name")]` | async funcのクロック指定 | +| `#[sv::pin("XX")]` | XDCピン割当 | +| `#[sv::iostandard("YY")]` | XDC IO標準 | + +--- + +## 11. SV予約語 (モジュール名回避) + +``` +output, input, inout, module, wire, reg, logic, begin, end, +if, else, for, while, case, default, assign, always, initial, +posedge, negedge, task, function, parameter, integer, real, time, event +``` + +--- + +## 12. テストベンチ自動生成 + +`generateTestbench()` が出力する構文: + +| SV構文 | 用途 | +|-------|------| +| `module _tb;` | テストベンチモジュール | +| `reg` | 入力信号宣言 | +| `wire` | 出力信号宣言 | +| ` uut(...)` | DUTインスタンス化 | +| `initial begin ... $finish; end` | テストシーケンス | +| `always #10 clk = ~clk;` | クロック生成 | +| `$dumpfile / $dumpvars` | 波形ダンプ | +| `$monitor` | 信号モニタリング | diff --git a/src/codegen/sv/codegen.cpp b/src/codegen/sv/codegen.cpp index 78ce0680..a71154d7 100644 --- a/src/codegen/sv/codegen.cpp +++ b/src/codegen/sv/codegen.cpp @@ -49,6 +49,21 @@ std::string SVCodeGen::mapType(const hir::TypePtr& type) const { if (type->element_type) return mapType(type->element_type); return "logic [31:0]"; + case hir::TypeKind::Bit: + return "logic"; // bit単体は1bit、bit[N]はArray処理で幅変換 + case hir::TypeKind::Array: + // bit[N] → logic [N-1:0] に変換 + if (type->element_type && type->element_type->kind == hir::TypeKind::Bit) { + if (type->array_size && *type->array_size > 1) { + return "logic [" + std::to_string(*type->array_size - 1) + ":0]"; + } + return "logic"; + } + // 通常の配列: element_type name [0:N-1] → element_typeだけ返す + if (type->element_type) { + return mapType(type->element_type); + } + return "logic [31:0]"; default: return "logic [31:0]"; // デフォルトは32bit } @@ -84,6 +99,17 @@ int SVCodeGen::getBitWidth(const hir::TypePtr& type) const { if (type->element_type) return getBitWidth(type->element_type); return 32; + case hir::TypeKind::Bit: + return 1; // bit単体は1bit + case hir::TypeKind::Array: + // bit[N] → Nビット + if (type->element_type && type->element_type->kind == hir::TypeKind::Bit) { + return type->array_size.value_or(1); + } + if (type->element_type) + return getBitWidth(type->element_type); + return 32; + // bit[N]配列型の場合はArray処理側でNを取得 default: return 32; } @@ -177,6 +203,14 @@ void SVCodeGen::emitModule(const SVModule& mod) { append_line(""); } + // typedef enum / struct packed 宣言 + for (const auto& td : mod.type_declarations) { + emitLine(td); + } + if (!mod.type_declarations.empty()) { + append_line(""); + } + // 内部ワイヤ宣言 for (const auto& wire : mod.wire_declarations) { emitLine(wire); @@ -203,11 +237,23 @@ void SVCodeGen::emitModule(const SVModule& mod) { append_line(""); } + // always_latch ブロック + for (const auto& block : mod.always_latch_blocks) { + emit(block); + append_line(""); + } + // assign 文 for (const auto& stmt : mod.assign_statements) { emitLine(stmt); } + // function/task ブロック + for (const auto& fn : mod.function_blocks) { + append_line(""); + emit(fn); + } + decreaseIndent(); emitLine("endmodule"); append_line(""); @@ -501,6 +547,102 @@ void SVCodeGen::analyzeFunction(const mir::MirFunction& func, SVModule& mod) { if (func.name == "main") return; + // 非always/非async関数 → SV function automatic または task automatic + // ただし、posedge/negedge以外の引数を持つ関数のみ + // 引数なし/posedge/negedge引数のみの関数はalwaysブロックとして出力 + if (!func.is_always && !func.is_async && func.always_kind == mir::MirFunction::AlwaysKind::None) { + // posedge/negedge以外の引数があるかチェック + bool has_sv_args = false; + for (auto arg_id : func.arg_locals) { + if (arg_id < func.locals.size()) { + auto& local = func.locals[arg_id]; + if (local.type && local.type->kind != hir::TypeKind::Posedge && + local.type->kind != hir::TypeKind::Negedge) { + has_sv_args = true; + break; + } + } + } + if (has_sv_args) { + std::ostringstream fn_ss; + indent_level_ = 1; + + // 戻り値型を取得 + bool is_void = true; + std::string ret_type_str = "void"; + if (func.return_local < func.locals.size()) { + auto& ret_local = func.locals[func.return_local]; + if (ret_local.type && ret_local.type->kind != hir::TypeKind::Void) { + is_void = false; + ret_type_str = mapType(ret_local.type); + } + } + + // 引数リスト構築(posedge/negedge型を除外) + std::vector args; + for (auto arg_id : func.arg_locals) { + if (arg_id < func.locals.size()) { + auto& local = func.locals[arg_id]; + if (local.type && (local.type->kind == hir::TypeKind::Posedge || + local.type->kind == hir::TypeKind::Negedge)) + continue; + args.push_back("input " + mapType(local.type) + " " + local.name); + } + } + + if (is_void) { + fn_ss << indent() << "task automatic " << func.name << "("; + } else { + fn_ss << indent() << "function automatic " << ret_type_str << " " << func.name << "("; + } + for (size_t i = 0; i < args.size(); ++i) { + if (i > 0) fn_ss << ", "; + fn_ss << args[i]; + } + fn_ss << ");\n"; + + // ローカル変数宣言(引数と戻り値を除く) + increaseIndent(); + std::set arg_set(func.arg_locals.begin(), func.arg_locals.end()); + for (size_t i = 0; i < func.locals.size(); ++i) { + if (i == func.return_local) continue; // 戻り値 + if (arg_set.count(static_cast(i))) continue; // 引数 + auto& local = func.locals[i]; + if (local.name.empty() || local.name.find('@') != std::string::npos) continue; + // ポインタ型テンポラリはスキップ(__builtin_* Call引数用) + if (local.name.find("_t") == 0 && local.type && + local.type->kind == hir::TypeKind::Pointer) continue; + fn_ss << indent() << mapType(local.type) << " " << local.name << ";\n"; + } + + // 関数本体 + if (!func.basic_blocks.empty() && func.basic_blocks[0]) { + std::set visited; + std::ostringstream body_ss; + emitBlockRecursive(func, 0, visited, body_ss); + // @return → return に置換 + std::string body = body_ss.str(); + size_t pos = 0; + while ((pos = body.find("@return", pos)) != std::string::npos) { + body.replace(pos, 7, func.name); + pos += func.name.size(); + } + fn_ss << body; + } + + decreaseIndent(); + + if (is_void) { + fn_ss << indent() << "endtask\n"; + } else { + fn_ss << indent() << "endfunction\n"; + } + + mod.function_blocks.push_back(fn_ss.str()); + return; + } // if (has_sv_args) + } + // ローカル変数を内部ワイヤ/レジスタとして宣言 // (ポートと名前が衝突する変数は除外) std::set port_names; @@ -558,26 +700,84 @@ void SVCodeGen::analyzeFunction(const mir::MirFunction& func, SVModule& mod) { std::string edge_clock; // クロック信号名 bool has_explicit_edge = false; + // 複数エッジ: 非同期リセット用 (always void f(posedge clk, negedge rst_n)) + std::vector> all_edges; // {edge_type, signal_name} + for (const auto& local : func.locals) { if (local.type && local.type->kind == hir::TypeKind::Posedge) { - edge_type = "posedge"; - edge_clock = local.name; - has_explicit_edge = true; - break; + // 重複排除: 同名信号が既にある場合はスキップ + bool dup = false; + for (const auto& e : all_edges) { + if (e.second == local.name) { dup = true; break; } + } + if (!dup) { + if (!has_explicit_edge) { + edge_type = "posedge"; + edge_clock = local.name; + has_explicit_edge = true; + } + all_edges.push_back({"posedge", local.name}); + } } if (local.type && local.type->kind == hir::TypeKind::Negedge) { - edge_type = "negedge"; - edge_clock = local.name; - has_explicit_edge = true; - break; + // 重複排除: 同名信号が既にある場合はスキップ + bool dup = false; + for (const auto& e : all_edges) { + if (e.second == local.name) { dup = true; break; } + } + if (!dup) { + if (!has_explicit_edge) { + edge_type = "negedge"; + edge_clock = local.name; + has_explicit_edge = true; + } + all_edges.push_back({"negedge", local.name}); + } } } if (has_explicit_edge) { // 明示的なposedge/negedge型パラメータ → always_ff - block_ss << indent() << "always_ff @(" << edge_type << " " << edge_clock << ") begin\n"; - } else if (func.is_async) { - // Phase 4: マルチクロックドメイン対応(後方互換: async func) + if (all_edges.size() > 1) { + // 複数エッジ: always_ff @(posedge clk or negedge rst_n) + block_ss << indent() << "always_ff @("; + for (size_t i = 0; i < all_edges.size(); ++i) { + if (i > 0) block_ss << " or "; + block_ss << all_edges[i].first << " " << all_edges[i].second; + } + block_ss << ") begin\n"; + } else { + block_ss << indent() << "always_ff @(" << edge_type << " " << edge_clock << ") begin\n"; + } + } else if (func.is_always && !has_explicit_edge) { + // always修飾子 + エッジパラメータなし + using AK = mir::MirFunction::AlwaysKind; + if (func.always_kind == AK::Comb) { + // always_comb 明示指定 + block_ss << indent() << "always_comb begin\n"; + } else if (func.always_kind == AK::Latch) { + // always_latch 明示指定 + block_ss << indent() << "always_latch begin\n"; + } else { + // AutoまたはNone: 後でCFG解析で判別(一旦always_combとして出力し後で置換) + block_ss << indent() << "always_comb begin\n"; + } + } else if (func.always_kind == mir::MirFunction::AlwaysKind::FF) { + // always_ff 明示指定(エッジパラメータなし)→ デフォルト posedge clk + std::string clock_name = "clk"; + for (const auto& attr : func.attributes) { + std::string prefix1 = "sv::clock_domain("; + std::string prefix2 = "verilog::clock_domain("; + if (attr.find(prefix1) == 0 && attr.back() == ')') { + clock_name = attr.substr(prefix1.size(), attr.size() - prefix1.size() - 1); + } else if (attr.find(prefix2) == 0 && attr.back() == ')') { + clock_name = attr.substr(prefix2.size(), attr.size() - prefix2.size() - 1); + } + } + block_ss << indent() << "always_ff @(posedge " << clock_name << ") begin\n"; + } else if (func.is_always || func.is_async) { + // always修飾子+エッジあり、またはasync修飾子(後方互換)→ always_ff @(posedge clk) + // Phase 4: マルチクロックドメイン対応 std::string clock_name = "clk"; for (const auto& attr : func.attributes) { std::string prefix1 = "sv::clock_domain("; @@ -1102,10 +1302,53 @@ void SVCodeGen::analyzeFunction(const mir::MirFunction& func, SVModule& mod) { block_content.pop_back(); } - if (has_explicit_edge || func.is_async) { + if (has_explicit_edge || func.is_async || + func.always_kind == mir::MirFunction::AlwaysKind::FF) { mod.always_ff_blocks.push_back(block_content); } else { - mod.always_comb_blocks.push_back(block_content); + using AK = mir::MirFunction::AlwaysKind; + if (func.always_kind == AK::Latch) { + // always_latch 明示指定 + mod.always_latch_blocks.push_back(block_content); + } else if (func.always_kind == AK::Comb) { + // always_comb 明示指定 + mod.always_comb_blocks.push_back(block_content); + } else { + // Auto: CFG解析で判別 + // 全制御パスで全出力が代入されていれば always_comb、 + // 一部パスで未代入があれば always_latch + // 簡易判定: ifがあってelseがない場合はラッチ推論 + bool has_incomplete_assign = false; + std::istringstream check_stream(block_content); + std::string check_line; + int if_count = 0; + int else_count = 0; + while (std::getline(check_stream, check_line)) { + // if begin の数と else begin の数をカウント + if (check_line.find("if (") != std::string::npos || + check_line.find("if(") != std::string::npos) { + if_count++; + } + if (check_line.find("end else begin") != std::string::npos || + check_line.find("else begin") != std::string::npos) { + else_count++; + } + } + // ifがあるのにelseが少ない → ラッチ推論 + if (if_count > 0 && else_count < if_count) { + has_incomplete_assign = true; + // ブロックヘッダを always_latch に置換 + size_t pos = block_content.find("always_comb begin"); + if (pos != std::string::npos) { + block_content.replace(pos, 17, "always_latch begin"); + } + } + if (has_incomplete_assign) { + mod.always_latch_blocks.push_back(block_content); + } else { + mod.always_comb_blocks.push_back(block_content); + } + } } // インデントレベルをリセット @@ -1311,9 +1554,117 @@ void SVCodeGen::emitTerminator(const mir::MirTerminator& term, const mir::MirFun case mir::MirTerminator::Unreachable: // SVのalwaysブロック内ではreturnは不要 break; - case mir::MirTerminator::Call: - // 関数呼び出し → Phase 2対応 + case mir::MirTerminator::Call: { + // __builtin_concat / __builtin_replicate をSV構文に変換 + const auto& cd = std::get(term.data); + std::string func_name; + if (cd.func && cd.func->kind == mir::MirOperand::FunctionRef) { + func_name = std::get(cd.func->data); + } + + if (func_name == "__builtin_concat" || func_name == "__builtin_replicate") { + // Ref逆引きマップ構築: テンポラリ(_tXXX) → 元のPlace + // Use(Constant)逆引きマップ: テンポラリ → 定数値 + // 先行Statement: Assign(_tXXX, Ref(original)) or Assign(_tXXX, Use(Constant)) を追跡 + std::map ref_map; + std::map> const_map; + for (const auto& block : func.basic_blocks) { + if (!block) continue; + for (const auto& s : block->statements) { + if (!s || s->kind != mir::MirStatement::Assign) continue; + const auto& ad = std::get(s->data); + if (!ad.rvalue) continue; + if (ad.rvalue->kind == mir::MirRvalue::Ref) { + if (auto* ref_data = std::get_if(&ad.rvalue->data)) { + ref_map.insert_or_assign(ad.place.local, ref_data->place); + } + } else if (ad.rvalue->kind == mir::MirRvalue::Use) { + // Use(Constant) パターン: _t = constant + if (auto* use_data = std::get_if(&ad.rvalue->data)) { + if (use_data->operand && use_data->operand->kind == mir::MirOperand::Constant) { + const_map.insert_or_assign(ad.place.local, + std::make_pair(std::get(use_data->operand->data), + use_data->operand->type)); + } + } + } + } + } + + // Call args を解決: テンポラリ → 元のPlace名 or 定数値 + auto resolveArg = [&](const mir::MirOperand& op) -> std::string { + if (op.kind == mir::MirOperand::Move || op.kind == mir::MirOperand::Copy) { + const auto& place = std::get(op.data); + // Ref逆引き: _t → &original → original + auto ref_it = ref_map.find(place.local); + if (ref_it != ref_map.end()) { + return emitPlace(ref_it->second, func); + } + // Const逆引き: _t → constant + auto const_it = const_map.find(place.local); + if (const_it != const_map.end()) { + return emitConstant(const_it->second.first, const_it->second.second); + } + return emitPlace(place, func); + } else if (op.kind == mir::MirOperand::Constant) { + return emitConstant(std::get(op.data), op.type); + } + return "0"; + }; + + // ノンブロッキング代入の判定 + bool use_nb = func.is_async; + if (!use_nb) { + for (const auto& local : func.locals) { + if (local.type && (local.type->kind == hir::TypeKind::Posedge || + local.type->kind == hir::TypeKind::Negedge)) { + use_nb = true; + break; + } + } + } + + if (func_name == "__builtin_concat") { + // SV連接: {a, b, ...} + std::string rhs = "{"; + for (size_t i = 0; i < cd.args.size(); ++i) { + if (i > 0) rhs += ", "; + rhs += cd.args[i] ? resolveArg(*cd.args[i]) : "0"; + } + rhs += "}"; + if (cd.destination) { + std::string lhs = emitPlace(*cd.destination, func); + ss << indent() << lhs << (use_nb ? " <= " : " = ") << rhs << ";\n"; + } + } else { + // SV複製: {N{expr}} + std::string count = cd.args.size() > 0 && cd.args[0] + ? resolveArg(*cd.args[0]) : "1"; + std::string expr = cd.args.size() > 1 && cd.args[1] + ? resolveArg(*cd.args[1]) : "0"; + // count は整数リテラルなので、SV幅指定(32'd3等)を除去して素の数字にする + // "32'd3" → "3", "3" → "3" + auto pos_tick = count.find("'d"); + if (pos_tick != std::string::npos) { + count = count.substr(pos_tick + 2); + } else { + pos_tick = count.find("'h"); + if (pos_tick != std::string::npos) { + count = count.substr(pos_tick + 2); + } + } + std::string rhs = "{" + count + "{" + expr + "}}"; + if (cd.destination) { + std::string lhs = emitPlace(*cd.destination, func); + ss << indent() << lhs << (use_nb ? " <= " : " = ") << rhs << ";\n"; + } + } + // 成功ブロックに続行 + emitBlockRecursive(func, cd.success, visited, ss, merge_block); + } + // その他の関数呼び出しはスキップ break; + } } } @@ -1394,6 +1745,32 @@ void SVCodeGen::analyzeMIR(const mir::MirProgram& program) { continue; } + // const変数 → localparam宣言 + if (gv->is_const) { + std::string localparam_decl = "localparam " + mapType(gv->type) + " " + gv->name; + // 初期値がある場合は付加 + if (gv->init_value) { + localparam_decl += " = " + emitConstant(*gv->init_value, gv->type); + } + localparam_decl += ";"; + default_mod.parameters.push_back(localparam_decl); + continue; + } + + // assign文 → wire宣言 + assign name = expr; + if (gv->is_assign) { + // wire宣言を追加 + default_mod.reg_declarations.push_back(mapType(gv->type) + " " + gv->name + ";"); + // assign文を追加 + std::string assign_stmt = "assign " + gv->name; + if (gv->init_value) { + assign_stmt += " = " + emitConstant(*gv->init_value, gv->type); + } + assign_stmt += ";"; + default_mod.assign_statements.push_back(assign_stmt); + continue; + } + // Phase 3: BRAM/LutRAM推論 bool is_bram = false; bool is_lutram = false; @@ -1462,6 +1839,49 @@ void SVCodeGen::analyzeMIR(const mir::MirProgram& program) { analyzeFunction(*func, default_mod); } + // enum → typedef enum logic 出力 + for (const auto& e : program.enums) { + if (!e) continue; + // Tagged Union(ペイロード付きenum)はSVでは直接変換しない + if (e->is_tagged_union()) continue; + + std::ostringstream ss; + // ビット幅計算: メンバー数から必要ビット数を算出 + int member_count = static_cast(e->members.size()); + int bit_width = 1; + int val = member_count - 1; + while (val > 1) { + bit_width++; + val >>= 1; + } + + ss << "typedef enum logic"; + if (bit_width > 1) { + ss << " [" << (bit_width - 1) << ":0]"; + } + ss << " {\n"; + for (size_t i = 0; i < e->members.size(); ++i) { + ss << " " << e->members[i].name << " = " << bit_width << "'d" << e->members[i].tag_value; + if (i + 1 < e->members.size()) ss << ","; + ss << "\n"; + } + ss << "} " << e->name << ";"; + default_mod.type_declarations.push_back(ss.str()); + } + + // struct → typedef struct packed 出力(#[sv::packed]属性付きのみ) + for (const auto& st : program.structs) { + if (!st) continue; + // TODO: sv::packed属性チェック(現状は全structをpacked出力) + std::ostringstream ss; + ss << "typedef struct packed {\n"; + for (const auto& f : st->fields) { + ss << " " << mapType(f.type) << " " << f.name << ";\n"; + } + ss << "} " << st->name << ";"; + default_mod.type_declarations.push_back(ss.str()); + } + modules_.push_back(default_mod); } @@ -1856,6 +2276,12 @@ bool SVCodeGen::validateSynthesizableTypes(const mir::MirProgram& program) { continue; switch (local.type->kind) { case hir::TypeKind::Pointer: + // MIR生成テンポラリ変数(_tXXX)はスキップ + // __builtin_concat等のCall引数用アドレステンポラリ + if (local.name.size() > 2 && local.name[0] == '_' && local.name[1] == 't' && + std::isdigit(static_cast(local.name[2]))) { + break; + } std::cerr << "error[SV002]: Pointer types not supported in SV target: " << func->name << "::" << local.name << "\n"; has_error = true; diff --git a/src/codegen/sv/codegen.hpp b/src/codegen/sv/codegen.hpp index 8147047f..1a1047a6 100644 --- a/src/codegen/sv/codegen.hpp +++ b/src/codegen/sv/codegen.hpp @@ -35,9 +35,12 @@ struct SVModule { std::string name; std::vector ports; std::vector parameters; // parameter宣言 - std::vector always_ff_blocks; // always_ff ブロック - std::vector always_comb_blocks; // always_comb ブロック + std::vector type_declarations; // typedef enum/struct packed 宣言 + std::vector always_ff_blocks; // always_ff ブロック + std::vector always_comb_blocks; // always_comb ブロック + std::vector always_latch_blocks; // always_latch ブロック std::vector assign_statements; // assign 文 + std::vector function_blocks; // function automatic ブロック std::vector wire_declarations; // 内部ワイヤ宣言 std::vector reg_declarations; // 内部レジスタ宣言 }; diff --git a/src/frontend/ast/decl.hpp b/src/frontend/ast/decl.hpp index d0c3e25d..7b7bb252 100644 --- a/src/frontend/ast/decl.hpp +++ b/src/frontend/ast/decl.hpp @@ -127,6 +127,10 @@ struct FunctionDecl { bool is_overload = false; // overload修飾子 bool is_extern = false; // extern "C" 関数 bool is_async = false; // async関数(JSバックエンド用) + bool is_always = false; // always修飾子(SVバックエンド用) + // SVバックエンド: always ブロックの種別 + // None=非always, Auto=自動判別, FF/Comb/Latch=明示指定 + enum class AlwaysKind { None, Auto, FF, Comb, Latch } always_kind = AlwaysKind::None; // ディレクティブ/アトリビュート(#test, #bench, #deprecated等) std::vector attributes; @@ -361,6 +365,7 @@ struct GlobalVarDecl { TypePtr type; ExprPtr init_expr; bool is_const = false; + bool is_assign = false; // SV assign文(連続代入) Visibility visibility = Visibility::Private; std::vector attributes; diff --git a/src/frontend/ast/types.hpp b/src/frontend/ast/types.hpp index eddb0d32..9af89b6f 100644 --- a/src/frontend/ast/types.hpp +++ b/src/frontend/ast/types.hpp @@ -57,6 +57,7 @@ enum class TypeKind { Negedge, // 立ち下がりエッジクロック信号 Wire, // wire修飾(組み合わせ出力) Reg, // reg修飾(レジスタ/順序回路出力) + Bit, // bit[N] 任意ビット幅型(1-bit単位) }; // ============================================================ @@ -301,6 +302,9 @@ inline TypePtr make_reg(TypePtr elem) { t->element_type = std::move(elem); return t; } +inline TypePtr make_bit() { + return std::make_shared(TypeKind::Bit); +} inline TypePtr make_pointer(TypePtr elem) { auto t = std::make_shared(TypeKind::Pointer); diff --git a/src/frontend/lexer/lexer.cpp b/src/frontend/lexer/lexer.cpp index 723cdac5..615f7376 100644 --- a/src/frontend/lexer/lexer.cpp +++ b/src/frontend/lexer/lexer.cpp @@ -159,6 +159,13 @@ void Lexer::add_sv_keywords() { {"negedge", TokenKind::KwNegedge}, {"wire", TokenKind::KwWire}, {"reg", TokenKind::KwReg}, + {"always", TokenKind::KwAlways}, + {"always_ff", TokenKind::KwAlwaysFF}, + {"always_comb", TokenKind::KwAlwaysComb}, + {"always_latch", TokenKind::KwAlwaysLatch}, + {"assign", TokenKind::KwAssign}, + {"initial", TokenKind::KwInitial}, + {"bit", TokenKind::KwBit}, }); } diff --git a/src/frontend/lexer/token.cpp b/src/frontend/lexer/token.cpp index 3b5b313a..8fc7bd7b 100644 --- a/src/frontend/lexer/token.cpp +++ b/src/frontend/lexer/token.cpp @@ -182,6 +182,20 @@ const char* token_kind_to_string(TokenKind kind) { return "wire"; case TokenKind::KwReg: return "reg"; + case TokenKind::KwAlways: + return "always"; + case TokenKind::KwAlwaysFF: + return "always_ff"; + case TokenKind::KwAlwaysComb: + return "always_comb"; + case TokenKind::KwAlwaysLatch: + return "always_latch"; + case TokenKind::KwAssign: + return "assign"; + case TokenKind::KwInitial: + return "initial"; + case TokenKind::KwBit: + return "bit"; // 演算子 case TokenKind::Plus: diff --git a/src/frontend/lexer/token.hpp b/src/frontend/lexer/token.hpp index 0deb9940..e44233fc 100644 --- a/src/frontend/lexer/token.hpp +++ b/src/frontend/lexer/token.hpp @@ -105,6 +105,13 @@ enum class TokenKind { KwNegedge, // negedge信号型 KwWire, // wire修飾型 KwReg, // reg修飾型 + KwAlways, // always ロジックブロック修飾子(自動判別) + KwAlwaysFF, // always_ff 順序回路(明示指定) + KwAlwaysComb, // always_comb 組み合わせ回路(明示指定) + KwAlwaysLatch, // always_latch ラッチ(明示指定) + KwAssign, // assign 連続代入 + KwInitial, // initial シミュレーション初期化 + KwBit, // bit 任意ビット幅型 // 演算子 Plus, diff --git a/src/frontend/parser/parser_decl.cpp b/src/frontend/parser/parser_decl.cpp index d82e380e..8d584a46 100644 --- a/src/frontend/parser/parser_decl.cpp +++ b/src/frontend/parser/parser_decl.cpp @@ -110,20 +110,44 @@ ast::DeclPtr Parser::parse_top_level() { } // export function (型から始まる関数、または修飾子から始まる関数の場合) - // 修飾子: static, inline, async + // 修飾子: static, inline, async, always, always_ff, always_comb, always_latch if (is_type_start() || check(TokenKind::KwStatic) || check(TokenKind::KwInline) || - check(TokenKind::KwAsync)) { + check(TokenKind::KwAsync) || check(TokenKind::KwAlways) || + check(TokenKind::KwAlwaysFF) || check(TokenKind::KwAlwaysComb) || + check(TokenKind::KwAlwaysLatch)) { // 修飾子を収集 bool is_static = consume_if(TokenKind::KwStatic); bool is_inline = consume_if(TokenKind::KwInline); bool is_async = consume_if(TokenKind::KwAsync); + bool is_always = consume_if(TokenKind::KwAlways); + // always_ff/always_comb/always_latch の明示指定 + auto ak = ast::FunctionDecl::AlwaysKind::None; + if (is_always) { + ak = ast::FunctionDecl::AlwaysKind::Auto; + } else if (consume_if(TokenKind::KwAlwaysFF)) { + is_always = true; + ak = ast::FunctionDecl::AlwaysKind::FF; + } else if (consume_if(TokenKind::KwAlwaysComb)) { + is_always = true; + ak = ast::FunctionDecl::AlwaysKind::Comb; + } else if (consume_if(TokenKind::KwAlwaysLatch)) { + is_always = true; + ak = ast::FunctionDecl::AlwaysKind::Latch; + } // グローバル変数判定(型 名前 = ... のパターン) - if (!is_static && !is_inline && !is_async && is_global_var_start()) { + if (!is_static && !is_inline && !is_async && !is_always && is_global_var_start()) { return parse_global_var_decl(true, std::move(attrs)); } - return parse_function(true, is_static, is_inline, std::move(attrs), is_async); + auto func_decl = parse_function(true, is_static, is_inline, std::move(attrs), is_async); + if (is_always) { + if (auto* f = func_decl->as()) { + f->is_always = true; + f->always_kind = ak; + } + } + return func_decl; } // それ以外は分離エクスポート (export NAME1, NAME2;) @@ -143,6 +167,30 @@ ast::DeclPtr Parser::parse_top_level() { bool is_static = consume_if(TokenKind::KwStatic); bool is_inline = consume_if(TokenKind::KwInline); bool is_async = consume_if(TokenKind::KwAsync); + bool is_always = consume_if(TokenKind::KwAlways); + // always_ff/always_comb/always_latch の明示指定 + auto ak = ast::FunctionDecl::AlwaysKind::None; + if (is_always) { + ak = ast::FunctionDecl::AlwaysKind::Auto; + } else if (consume_if(TokenKind::KwAlwaysFF)) { + is_always = true; + ak = ast::FunctionDecl::AlwaysKind::FF; + } else if (consume_if(TokenKind::KwAlwaysComb)) { + is_always = true; + ak = ast::FunctionDecl::AlwaysKind::Comb; + } else if (consume_if(TokenKind::KwAlwaysLatch)) { + is_always = true; + ak = ast::FunctionDecl::AlwaysKind::Latch; + } + + // SV assign文: assign type name = expr; + if (consume_if(TokenKind::KwAssign)) { + auto gv = parse_global_var_decl(false, std::move(attrs)); + if (auto* g = gv->as()) { + g->is_assign = true; + } + return gv; + } // struct if (check(TokenKind::KwStruct)) { @@ -220,12 +268,19 @@ ast::DeclPtr Parser::parse_top_level() { } // グローバル変数判定(型 名前 = ... のパターン) - if (!is_static && !is_inline && !is_async && is_global_var_start()) { + if (!is_static && !is_inline && !is_async && !is_always && is_global_var_start()) { return parse_global_var_decl(false, std::move(attrs)); } // 関数 (型 名前 ...) - return parse_function(false, is_static, is_inline, std::move(attrs), is_async); + auto func_decl = parse_function(false, is_static, is_inline, std::move(attrs), is_async); + if (is_always) { + if (auto* f = func_decl->as()) { + f->is_always = true; + f->always_kind = ak; + } + } + return func_decl; } // グローバル変数宣言かどうかを先読みで判定 @@ -262,6 +317,18 @@ bool Parser::is_global_var_start() { advance(); + // 配列サフィックス [N] をスキップ(bit[4], utiny[1024] 等) + while (!is_at_end() && check(TokenKind::LBracket)) { + advance(); // [ + if (!is_at_end() && check(TokenKind::IntLiteral)) { + advance(); // N + } + if (!is_at_end() && check(TokenKind::RBracket)) { + advance(); // ] + } + } + + // ポインタ修飾子 * をスキップ while (!is_at_end() && check(TokenKind::Star)) { advance(); } @@ -269,7 +336,8 @@ bool Parser::is_global_var_start() { bool result = false; if (!is_at_end() && check(TokenKind::Ident)) { advance(); - if (!is_at_end() && check(TokenKind::Eq)) { + // 初期化子あり (=) または初期化子なし (;) の両方をサポート + if (!is_at_end() && (check(TokenKind::Eq) || check(TokenKind::Semicolon))) { result = true; } } diff --git a/src/frontend/parser/parser_expr.cpp b/src/frontend/parser/parser_expr.cpp index 8b23c45e..67ad2251 100644 --- a/src/frontend/parser/parser_expr.cpp +++ b/src/frontend/parser/parser_expr.cpp @@ -1012,40 +1012,97 @@ ast::ExprPtr Parser::parse_primary() { return ast::make_array_literal(std::move(elements), Span{start_pos, previous().end}); } - // 暗黙的構造体リテラル: {field1: val1, field2: val2, ...} - // 型は文脈から推論される - if (consume_if(TokenKind::LBrace)) { - debug::par::log(debug::par::Id::PrimaryExpr, "Found implicit struct literal", - debug::Level::Debug); - std::vector fields; + // {expr, ...} / {N{expr}} / {field: val, ...} + // 3パターンの判別: + // (1) {ident: expr, ...} → 構造体リテラル (colonあり) + // (2) {N{expr}} → 複製 (intリテラル + LBrace) + // (3) {expr, expr, ...} → 連接 (カンマ区切りの式) + if (check(TokenKind::LBrace)) { + // 先読みで構造体リテラルかどうかを判別 + auto saved_pos = pos_; + advance(); // { を消費 + + // 空の {} はスキップ(ブロックとして扱う) + if (check(TokenKind::RBrace)) { + pos_ = saved_pos; + // 通常のブロック式として処理をフォールスルー + } + // パターン2: {N{expr}} → 複製式 + else if (check(TokenKind::IntLiteral)) { + auto int_pos = pos_; + int64_t count = current().get_int(); + advance(); // intリテラルを消費 + if (check(TokenKind::LBrace)) { + advance(); // 内側の { を消費 + auto inner_expr = parse_expr(); + expect(TokenKind::RBrace); // 内側の } + expect(TokenKind::RBrace); // 外側の } + // __builtin_replicate(count, expr) として表現 + auto callee = ast::make_ident("__builtin_replicate", Span{start_pos, start_pos}); + std::vector args; + args.push_back(ast::make_int_literal(count, Span{start_pos, start_pos})); + args.push_back(std::move(inner_expr)); + return ast::make_call(std::move(callee), std::move(args), + Span{start_pos, previous().end}); + } + // intリテラルの後にLBraceがない → 連接として解析 + pos_ = int_pos; + // フォールスルーして連接として解析 + goto parse_concat; + } + // パターン1: {ident: ...} → 構造体リテラル + else if (check(TokenKind::Ident)) { + auto ident_pos = pos_; + advance(); // ident を消費 + if (check(TokenKind::Colon)) { + // 構造体リテラル確定 + pos_ = saved_pos; + advance(); // { を再消費 + debug::par::log(debug::par::Id::PrimaryExpr, "Found implicit struct literal", + debug::Level::Debug); + std::vector fields; - if (!check(TokenKind::RBrace)) { - do { - // フィールド名:値 形式のみ(名前付き初期化必須) - if (!check(TokenKind::Ident)) { - error("Expected field name in struct literal (named initialization required)"); - } + if (!check(TokenKind::RBrace)) { + do { + if (!check(TokenKind::Ident)) { + error("Expected field name in struct literal (named initialization required)"); + } - std::string field_name(current().get_string()); - advance(); // フィールド名を消費 + std::string field_name(current().get_string()); + advance(); - if (!check(TokenKind::Colon)) { - error("Expected ':' after field name '" + field_name + "' in struct literal"); + if (!check(TokenKind::Colon)) { + error("Expected ':' after field name '" + field_name + "' in struct literal"); + } + advance(); + + auto value = parse_expr(); + fields.emplace_back(std::move(field_name), std::move(value)); + } while (consume_if(TokenKind::Comma)); } - advance(); // : を消費 - auto value = parse_expr(); - fields.emplace_back(std::move(field_name), std::move(value)); - } while (consume_if(TokenKind::Comma)); + expect(TokenKind::RBrace); + return ast::make_struct_literal("", std::move(fields), + Span{start_pos, previous().end}); + } + // ident の後に : がない → 連接として解析 + pos_ = ident_pos; + goto parse_concat; + } + // パターン3: {expr, expr, ...} → 連接式 + else { + parse_concat: + // 式をカンマ区切りでパースして __builtin_concat に変換 + std::vector elements; + elements.push_back(parse_expr()); + while (consume_if(TokenKind::Comma)) { + elements.push_back(parse_expr()); + } + expect(TokenKind::RBrace); + auto callee = ast::make_ident("__builtin_concat", Span{start_pos, start_pos}); + return ast::make_call(std::move(callee), std::move(elements), + Span{start_pos, previous().end}); } - - expect(TokenKind::RBrace); - debug::par::log( - debug::par::Id::PrimaryExpr, - "Created implicit struct literal with " + std::to_string(fields.size()) + " fields", - debug::Level::Debug); - // 型名は空文字列(型推論で解決) - return ast::make_struct_literal("", std::move(fields), Span{start_pos, previous().end}); } // 括弧式またはラムダ式 diff --git a/src/frontend/parser/parser_stmt.cpp b/src/frontend/parser/parser_stmt.cpp index 589d11c4..8ceed324 100644 --- a/src/frontend/parser/parser_stmt.cpp +++ b/src/frontend/parser/parser_stmt.cpp @@ -424,6 +424,7 @@ bool Parser::is_type_start() { case TokenKind::KwNegedge: case TokenKind::KwWire: case TokenKind::KwReg: + case TokenKind::KwBit: return true; case TokenKind::Star: // *type name の形式かチェック(*p = x のような式と区別) diff --git a/src/frontend/parser/parser_type.cpp b/src/frontend/parser/parser_type.cpp index 16b87101..5160558a 100644 --- a/src/frontend/parser/parser_type.cpp +++ b/src/frontend/parser/parser_type.cpp @@ -243,6 +243,10 @@ ast::TypePtr Parser::parse_type() { advance(); base_type = ast::make_reg(parse_type()); break; + case TokenKind::KwBit: + advance(); + base_type = ast::make_bit(); + break; default: break; } diff --git a/src/frontend/types/checking/call.cpp b/src/frontend/types/checking/call.cpp index 3049b2c0..1994fbb2 100644 --- a/src/frontend/types/checking/call.cpp +++ b/src/frontend/types/checking/call.cpp @@ -106,6 +106,22 @@ ast::TypePtr TypeChecker::infer_call(ast::CallExpr& call) { return ast::make_named(ident->name); } + // SVバックエンド用ビルトイン関数のバイパス + if (ident->name == "__builtin_concat" || ident->name == "__builtin_replicate") { + ast::TypePtr result_type = nullptr; + for (size_t i = 0; i < call.args.size(); ++i) { + auto t = infer_type(*call.args[i]); + // __builtin_replicate: 2番目の引数(複製対象)の型を使用 + // __builtin_concat: 最初の引数の型を使用 + if (ident->name == "__builtin_replicate") { + if (i == 1) result_type = t; // 2番目の引数の型 + } else { + if (!result_type) result_type = t; + } + } + return result_type ? result_type : ast::make_void(); + } + // 通常の関数はシンボルテーブルから検索 auto sym = scopes_.current().lookup(ident->name); if (!sym) { diff --git a/src/hir/lowering/decl.cpp b/src/hir/lowering/decl.cpp index bdc59dc5..31bd6cbf 100644 --- a/src/hir/lowering/decl.cpp +++ b/src/hir/lowering/decl.cpp @@ -62,6 +62,15 @@ HirDeclPtr HirLowering::lower_function(ast::FunctionDecl& func) { hir_func->is_export = func.visibility == ast::Visibility::Export; hir_func->is_extern = func.is_extern; // externフラグを伝播 hir_func->is_async = func.is_async; // asyncフラグを伝播 + hir_func->is_always = func.is_always; // alwaysフラグを伝播 + // always_kind を伝搬(AST→HIR: enum値をintでキャスト) + hir_func->always_kind = static_cast( + static_cast(func.always_kind)); + + // SV属性を伝播(sv::latch, sv::clock_domain等) + for (const auto& attr : func.attributes) { + hir_func->attributes.push_back(attr.name); + } // ジェネリックパラメータを処理 for (const auto& param_name : func.generic_params) { @@ -434,6 +443,7 @@ HirDeclPtr HirLowering::lower_global_var(ast::GlobalVarDecl& gv) { hir_global->name = gv.name; hir_global->type = gv.type; hir_global->is_const = gv.is_const; + hir_global->is_assign = gv.is_assign; hir_global->is_export = (gv.visibility == ast::Visibility::Export); // 属性を伝搬(#[input], #[output] 等、SV用) diff --git a/src/hir/lowering/expr.cpp b/src/hir/lowering/expr.cpp index 74550cd1..9c8da621 100644 --- a/src/hir/lowering/expr.cpp +++ b/src/hir/lowering/expr.cpp @@ -716,8 +716,10 @@ HirExprPtr HirLowering::lower_call(ast::CallExpr& call, TypePtr type) { hir->func_name = func_name; debug::hir::log(debug::hir::Id::CallTarget, "function: " + func_name, debug::Level::Trace); - static const std::set builtin_funcs = {"printf", "__println__", "__print__", - "sprintf", "exit", "panic"}; + static const std::set builtin_funcs = { + "printf", "__println__", "__print__", + "sprintf", "exit", "panic", + "__builtin_concat", "__builtin_replicate"}; bool is_builtin = builtin_funcs.find(func_name) != builtin_funcs.end(); bool is_defined = func_defs_.find(func_name) != func_defs_.end(); diff --git a/src/hir/nodes.hpp b/src/hir/nodes.hpp index 6f0346a4..850aea37 100644 --- a/src/hir/nodes.hpp +++ b/src/hir/nodes.hpp @@ -383,7 +383,9 @@ struct HirFunction { bool is_destructor = false; bool is_static = false; // staticメソッド(selfパラメータなし) bool is_async = false; // async関数(JSバックエンド用) - bool is_overload = false; // overloadキーワードの有無 + bool is_always = false; // always修飾子(SVバックエンド用) + enum class AlwaysKind { None, Auto, FF, Comb, Latch } always_kind = AlwaysKind::None; + std::vector attributes; // SV属性(sv::latch, sv::clock_domain等) HirMethodAccess access = HirMethodAccess::Public; // メソッドの場合のアクセス修飾子 }; @@ -522,6 +524,7 @@ struct HirGlobalVar { TypePtr type; HirExprPtr init; bool is_const; + bool is_assign = false; // SV assign文(連続代入) bool is_export = false; std::vector attributes; // "input", "output" 等(SV用) }; diff --git a/src/main.cpp b/src/main.cpp index 2821954a..62bd1f3a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -63,7 +63,7 @@ std::string get_version() { #ifdef CM_VERSION return CM_VERSION; #else - return "0.15.0"; + return "0.15.1"; #endif } diff --git a/src/mir/lowering/base.cpp b/src/mir/lowering/base.cpp index 15eeca0b..4b522136 100644 --- a/src/mir/lowering/base.cpp +++ b/src/mir/lowering/base.cpp @@ -172,7 +172,8 @@ void MirLoweringBase::register_global_var(const hir::HirGlobalVar& gv) { if (const_val) { const_val->type = gv.type ? gv.type : const_val->type; global_const_values[gv.name] = *const_val; - return; + // SVバックエンドではlocalparam出力のため、global_varsにも登録する + // (returnせずフォールスルーで下のMirGlobalVar登録へ進む) } } @@ -181,6 +182,7 @@ void MirLoweringBase::register_global_var(const hir::HirGlobalVar& gv) { mir_gv->name = gv.name; mir_gv->type = gv.type; mir_gv->is_const = gv.is_const; + mir_gv->is_assign = gv.is_assign; mir_gv->is_export = gv.is_export; mir_gv->attributes = gv.attributes; // SV用属性を伝搬(input/output等) diff --git a/src/mir/lowering/impl.cpp b/src/mir/lowering/impl.cpp index 0a9464dc..30fd4955 100644 --- a/src/mir/lowering/impl.cpp +++ b/src/mir/lowering/impl.cpp @@ -156,6 +156,11 @@ std::unique_ptr MirLowering::lower_function(const hir::HirFunction& mir_func->is_extern = func.is_extern; // externフラグを設定 mir_func->is_variadic = func.is_variadic; // 可変長引数フラグを設定 mir_func->is_async = func.is_async; // asyncフラグを設定 + mir_func->is_always = func.is_always; // alwaysフラグを設定 + // always_kind を伝搬(HIR→MIR: enum値をintでキャスト) + mir_func->always_kind = static_cast( + static_cast(func.always_kind)); + mir_func->attributes = func.attributes; // SV属性を伝搬(sv::latch等) // 戻り値用のローカル変数(typedefを解決) mir_func->return_local = 0; diff --git a/src/mir/nodes.hpp b/src/mir/nodes.hpp index a2157804..cbf460c1 100644 --- a/src/mir/nodes.hpp +++ b/src/mir/nodes.hpp @@ -635,6 +635,9 @@ struct MirFunction { bool is_extern = false; // extern "C" 関数か bool is_variadic = false; // 可変長引数(FFI用) bool is_async = false; // async関数(JSバックエンド用) + bool is_always = false; // always修飾子(SVバックエンド用: always_ff/always_comb) + // SVバックエンド: always ブロックの種別 + enum class AlwaysKind { None, Auto, FF, Comb, Latch } always_kind = AlwaysKind::None; std::vector attributes; // SV属性(clock_domain, pipeline等) std::vector locals; // ローカル変数(引数も含む) std::vector arg_locals; // 引数に対応するローカルID @@ -881,6 +884,7 @@ struct MirGlobalVar { hir::TypePtr type; std::unique_ptr init_value; // 初期値(nullptrならゼロ初期化) bool is_const = false; + bool is_assign = false; // SV assign文(連続代入) bool is_export = false; std::vector attributes; // "input", "output" 等(SV用) }; diff --git a/tests/sv/advanced/always_async_reset.cm b/tests/sv/advanced/always_async_reset.cm new file mode 100644 index 00000000..cdabfbee --- /dev/null +++ b/tests/sv/advanced/always_async_reset.cm @@ -0,0 +1,16 @@ +//! platform: sv + +// always + 非同期リセット (複数エッジ) テスト +// always_ff @(posedge clk or negedge rst_n) の生成確認 + +#[input] bool clk = 0; +#[input] bool rst_n = 1; +#[output] uint count = 0; + +always void process(posedge clk, negedge rst_n) { + if (rst_n == false) { + count = 0; + } else { + count = count + 1; + } +} diff --git a/tests/sv/advanced/always_async_reset.expect b/tests/sv/advanced/always_async_reset.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/advanced/always_async_reset.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/advanced/always_auto_latch.cm b/tests/sv/advanced/always_auto_latch.cm new file mode 100644 index 00000000..a5675e2c --- /dev/null +++ b/tests/sv/advanced/always_auto_latch.cm @@ -0,0 +1,13 @@ +//! platform: sv + +// always 自動判別テスト: always (if without else) → always_latch に自動変換 + +#[input] bool wr_en = false; +#[input] uint data_in = 0; +#[output] uint data_out = 0; + +always void auto_latch() { + if (wr_en) { + data_out = data_in; + } +} diff --git a/tests/sv/advanced/always_auto_latch.expect b/tests/sv/advanced/always_auto_latch.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/advanced/always_auto_latch.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/advanced/always_comb_explicit.cm b/tests/sv/advanced/always_comb_explicit.cm new file mode 100644 index 00000000..d75bb11a --- /dev/null +++ b/tests/sv/advanced/always_comb_explicit.cm @@ -0,0 +1,13 @@ +//! platform: sv + +// always_comb 明示テスト: always_comb キーワード直接指定 + +#[input] bool a = false; +#[input] bool b = false; +#[output] bool and_out = false; +#[output] bool or_out = false; + +always_comb void logic_gates() { + and_out = a && b; + or_out = a || b; +} diff --git a/tests/sv/advanced/always_comb_explicit.expect b/tests/sv/advanced/always_comb_explicit.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/advanced/always_comb_explicit.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/advanced/always_comb_mux.cm b/tests/sv/advanced/always_comb_mux.cm new file mode 100644 index 00000000..fc34c4c5 --- /dev/null +++ b/tests/sv/advanced/always_comb_mux.cm @@ -0,0 +1,16 @@ +//! platform: sv + +// always修飾子テスト: エッジなし → always_comb 生成確認 + +#[input] bool sel = 0; +#[input] uint a = 0; +#[input] uint b = 0; +#[output] uint out = 0; + +always void select() { + if (sel) { + out = a; + } else { + out = b; + } +} diff --git a/tests/sv/advanced/always_comb_mux.expect b/tests/sv/advanced/always_comb_mux.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/advanced/always_comb_mux.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/advanced/always_counter.cm b/tests/sv/advanced/always_counter.cm new file mode 100644 index 00000000..ca87538a --- /dev/null +++ b/tests/sv/advanced/always_counter.cm @@ -0,0 +1,15 @@ +//! platform: sv + +// always修飾子テスト: always_ff @(posedge clk) 生成確認 + +#[input] bool clk = 0; +#[input] bool rst = 0; +#[output] uint count = 0; + +always void tick(posedge clk) { + if (rst) { + count = 0; + } else { + count = count + 1; + } +} diff --git a/tests/sv/advanced/always_counter.expect b/tests/sv/advanced/always_counter.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/advanced/always_counter.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/advanced/always_ff_explicit.cm b/tests/sv/advanced/always_ff_explicit.cm new file mode 100644 index 00000000..2946face --- /dev/null +++ b/tests/sv/advanced/always_ff_explicit.cm @@ -0,0 +1,11 @@ +//! platform: sv + +// always_ff 明示テスト: always_ff キーワード直接指定 + +#[input] posedge clk; +#[input] uint data_in = 0; +#[output] uint data_out = 0; + +always_ff void ff_block(posedge clk) { + data_out = data_in; +} diff --git a/tests/sv/advanced/always_ff_explicit.expect b/tests/sv/advanced/always_ff_explicit.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/advanced/always_ff_explicit.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/advanced/backward_compat_async.cm b/tests/sv/advanced/backward_compat_async.cm new file mode 100644 index 00000000..501d8800 --- /dev/null +++ b/tests/sv/advanced/backward_compat_async.cm @@ -0,0 +1,22 @@ +//! platform: sv + +// 後方互換テスト: 旧構文async funcが引き続きalways_ff @(posedge clk)になる + +#[input] bool clk = 0; +#[input] bool rst = 0; +#[output] uint count = 0; +#[output] bool led = false; + +// 後方互換: async func → always_ff @(posedge clk) +async func tick() { + if (rst) { + count = 0; + led = false; + } else { + count = count + 1; + if (count == 25000000) { + count = 0; + led = !led; + } + } +} diff --git a/tests/sv/advanced/backward_compat_async.expect b/tests/sv/advanced/backward_compat_async.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/advanced/backward_compat_async.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/advanced/backward_compat_comb.cm b/tests/sv/advanced/backward_compat_comb.cm new file mode 100644 index 00000000..7b8c1aeb --- /dev/null +++ b/tests/sv/advanced/backward_compat_comb.cm @@ -0,0 +1,16 @@ +//! platform: sv + +// 後方互換テスト: void f() (エッジなし) が always_comb になる + +#[input] uint a = 0; +#[input] uint b = 0; +#[output] uint max_val = 0; + +// 後方互換: void f() → always_comb +void find_max() { + if (a > b) { + max_val = a; + } else { + max_val = b; + } +} diff --git a/tests/sv/advanced/backward_compat_comb.expect b/tests/sv/advanced/backward_compat_comb.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/advanced/backward_compat_comb.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/advanced/backward_compat_posedge.cm b/tests/sv/advanced/backward_compat_posedge.cm new file mode 100644 index 00000000..86d9a96f --- /dev/null +++ b/tests/sv/advanced/backward_compat_posedge.cm @@ -0,0 +1,16 @@ +//! platform: sv + +// 後方互換テスト: void f(posedge clk)が引き続きalways_ff @(posedge clk)になる + +#[input] bool clk = 0; +#[input] bool rst = 0; +#[output] utiny shift = 1; + +// 後方互換: void f(posedge clk) → always_ff @(posedge clk) +void shifter(posedge clk) { + if (rst) { + shift = 1; + } else { + shift = (shift << 1) | (shift >> 7); + } +} diff --git a/tests/sv/advanced/backward_compat_posedge.expect b/tests/sv/advanced/backward_compat_posedge.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/advanced/backward_compat_posedge.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/advanced/clock_domain.cm b/tests/sv/advanced/clock_domain.cm new file mode 100644 index 00000000..7b10f314 --- /dev/null +++ b/tests/sv/advanced/clock_domain.cm @@ -0,0 +1,16 @@ +//! platform: sv + +// #[sv::clock_domain] テスト: カスタムクロック名 + +#[input] bool sys_clk = 0; +#[input] bool rst = 0; +#[output] uint count = 0; + +#[sv::clock_domain("sys_clk")] +always void tick() { + if (rst) { + count = 0; + } else { + count = count + 1; + } +} diff --git a/tests/sv/advanced/clock_domain.expect b/tests/sv/advanced/clock_domain.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/advanced/clock_domain.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/advanced/concat_replicate.cm b/tests/sv/advanced/concat_replicate.cm new file mode 100644 index 00000000..f2aa384e --- /dev/null +++ b/tests/sv/advanced/concat_replicate.cm @@ -0,0 +1,13 @@ +//! platform: sv + +// SV連接/複製/ビットスライス テスト + +#[input] bit[4] a = 0; +#[input] bit[4] b = 0; +#[output] bit[8] result = 0; +#[output] bit[12] replicated = 0; + +always_comb void compute() { + result = {a, b}; + replicated = {3{a}}; +} diff --git a/tests/sv/advanced/concat_replicate.expect b/tests/sv/advanced/concat_replicate.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/advanced/concat_replicate.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/advanced/const_expr.cm b/tests/sv/advanced/const_expr.cm new file mode 100644 index 00000000..907af86a --- /dev/null +++ b/tests/sv/advanced/const_expr.cm @@ -0,0 +1,21 @@ +//! platform: sv + +// const式演算テスト: 定数式の演算がlocalparamとして出力されるか確認 + +const uint BASE_FREQ = 27000000; +const uint HALF_FREQ = BASE_FREQ / 2; +const uint BAUD_DIV = BASE_FREQ / 115200; +const uint MASK_UPPER = 0xFF00; +const uint MASK_LOWER = 0x00FF; +const uint COMBINED = MASK_UPPER | MASK_LOWER; + +#[input] bool clk = 0; +#[output] uint divider = 0; + +always void tick(posedge clk) { + if (divider == BAUD_DIV) { + divider = 0; + } else { + divider = divider + 1; + } +} diff --git a/tests/sv/advanced/const_expr.expect b/tests/sv/advanced/const_expr.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/advanced/const_expr.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/advanced/enum_typedef.cm b/tests/sv/advanced/enum_typedef.cm new file mode 100644 index 00000000..2199587f --- /dev/null +++ b/tests/sv/advanced/enum_typedef.cm @@ -0,0 +1,23 @@ +//! platform: sv + +// enum → typedef enum logic テスト +// CmのenumをSVのtypedef enumに変換 + +enum State { + IDLE, + RUN, + DONE, + ERROR +} + +#[input] bool clk = false; +#[input] bool rst_n = true; +#[output] uint count = 0; + +always void process(posedge clk, negedge rst_n) { + if (rst_n == false) { + count = 0; + } else { + count = count + 1; + } +} diff --git a/tests/sv/advanced/enum_typedef.expect b/tests/sv/advanced/enum_typedef.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/advanced/enum_typedef.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/advanced/latch_explicit.cm b/tests/sv/advanced/latch_explicit.cm new file mode 100644 index 00000000..8839d891 --- /dev/null +++ b/tests/sv/advanced/latch_explicit.cm @@ -0,0 +1,13 @@ +//! platform: sv + +// always_latch テスト: always_latch キーワードで明示的にラッチ指定 + +#[input] bool enable = false; +#[input] uint data_in = 0; +#[output] uint data_out = 0; + +always_latch void latch_process() { + if (enable) { + data_out = data_in; + } +} diff --git a/tests/sv/advanced/latch_explicit.expect b/tests/sv/advanced/latch_explicit.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/advanced/latch_explicit.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/advanced/localparam_const.cm b/tests/sv/advanced/localparam_const.cm new file mode 100644 index 00000000..5185daad --- /dev/null +++ b/tests/sv/advanced/localparam_const.cm @@ -0,0 +1,23 @@ +//! platform: sv + +// const → localparam テスト + +const uint CLK_DIV = 27000000; +const utiny STATE_IDLE = 0; +const utiny STATE_RUN = 1; + +#[input] bool clk = 0; +#[input] bool rst = 0; +#[output] uint count = 0; + +always void tick(posedge clk) { + if (rst) { + count = 0; + } else { + if (count == CLK_DIV) { + count = 0; + } else { + count = count + 1; + } + } +} diff --git a/tests/sv/advanced/localparam_const.expect b/tests/sv/advanced/localparam_const.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/advanced/localparam_const.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/advanced/mixed_always.cm b/tests/sv/advanced/mixed_always.cm new file mode 100644 index 00000000..9735f6e9 --- /dev/null +++ b/tests/sv/advanced/mixed_always.cm @@ -0,0 +1,28 @@ +//! platform: sv + +// always_ff + always_comb 混在テスト +// 1モジュール内に順序回路と組み合わせ回路を共存 + +#[input] bool clk = 0; +#[input] bool rst = 0; +#[input] bool enable = 0; +#[output] uint count = 0; +#[output] bool overflow = false; + +always void counter(posedge clk) { + if (rst) { + count = 0; + } else { + if (enable) { + count = count + 1; + } + } +} + +always void detect_overflow() { + if (count > 1000) { + overflow = true; + } else { + overflow = false; + } +} diff --git a/tests/sv/advanced/mixed_always.expect b/tests/sv/advanced/mixed_always.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/advanced/mixed_always.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/advanced/multi_always_comb.cm b/tests/sv/advanced/multi_always_comb.cm new file mode 100644 index 00000000..37a4f77e --- /dev/null +++ b/tests/sv/advanced/multi_always_comb.cm @@ -0,0 +1,18 @@ +//! platform: sv + +// always_comb 複数ブロックテスト +// 1つのモジュール内に複数のalways_combブロックを定義 + +#[input] bool sel = 0; +#[input] uint a = 0; +#[input] uint b = 0; +#[output] uint sum = 0; +#[output] uint diff = 0; + +always void calc_sum() { + sum = a + b; +} + +always void calc_diff() { + diff = a - b; +} diff --git a/tests/sv/advanced/multi_always_comb.expect b/tests/sv/advanced/multi_always_comb.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/advanced/multi_always_comb.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/advanced/struct_packed.cm b/tests/sv/advanced/struct_packed.cm new file mode 100644 index 00000000..2af32732 --- /dev/null +++ b/tests/sv/advanced/struct_packed.cm @@ -0,0 +1,16 @@ +//! platform: sv + +// struct → typedef struct packed テスト + +struct Pixel { + utiny r; + utiny g; + utiny b; +} + +#[input] bool clk = false; +#[output] uint brightness = 0; + +always void process(posedge clk) { + brightness = brightness + 1; +} diff --git a/tests/sv/advanced/struct_packed.expect b/tests/sv/advanced/struct_packed.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/advanced/struct_packed.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/advanced/sv_function.cm b/tests/sv/advanced/sv_function.cm new file mode 100644 index 00000000..2106d31b --- /dev/null +++ b/tests/sv/advanced/sv_function.cm @@ -0,0 +1,19 @@ +//! platform: sv + +// function テスト +// 通常の非always関数は SV function に変換 + +#[input] uint a = 0; +#[input] uint b = 0; +#[output] uint result = 0; + +uint max_val(uint x, uint y) { + if (x > y) { + return x; + } + return y; +} + +always_comb void compute() { + result = a; +} diff --git a/tests/sv/advanced/sv_function.expect b/tests/sv/advanced/sv_function.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/advanced/sv_function.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/advanced/sv_param.cm b/tests/sv/advanced/sv_param.cm new file mode 100644 index 00000000..05cb3a62 --- /dev/null +++ b/tests/sv/advanced/sv_param.cm @@ -0,0 +1,18 @@ +//! platform: sv + +// #[sv::param] テスト: parameter宣言の生成確認 + +#[sv::param] const uint WIDTH = 8; +#[sv::param] const uint DEPTH = 256; + +#[input] bool clk = 0; +#[input] bool we = 0; +#[input] uint addr = 0; +#[input] uint wdata = 0; +#[output] uint rdata = 0; + +always void read_proc(posedge clk) { + if (we) { + rdata = wdata; + } +} diff --git a/tests/sv/advanced/sv_param.expect b/tests/sv/advanced/sv_param.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/advanced/sv_param.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/advanced/uart_counter.cm b/tests/sv/advanced/uart_counter.cm new file mode 100644 index 00000000..d4fc04a8 --- /dev/null +++ b/tests/sv/advanced/uart_counter.cm @@ -0,0 +1,44 @@ +//! platform: sv + +// const + always + 複雑な制御フローテスト +// UART風カウンタ: 定数、ネストif/else、算術演算の組み合わせ + +const uint CLK_FREQ = 50000000; +const uint TARGET_BAUD = 9600; +const uint BAUD_DIV = CLK_FREQ / TARGET_BAUD; +const utiny BIT_COUNT = 8; + +#[input] bool clk = 0; +#[input] bool rst = 0; +#[input] bool start = 0; +#[output] uint baud_counter = 0; +#[output] utiny bit_index = 0; +#[output] bool tx_busy = false; + +always void uart_tick(posedge clk) { + if (rst) { + baud_counter = 0; + bit_index = 0; + tx_busy = false; + } else { + if (tx_busy) { + if (baud_counter == BAUD_DIV) { + baud_counter = 0; + if (bit_index == BIT_COUNT) { + bit_index = 0; + tx_busy = false; + } else { + bit_index = bit_index + 1; + } + } else { + baud_counter = baud_counter + 1; + } + } else { + if (start) { + tx_busy = true; + baud_counter = 0; + bit_index = 0; + } + } + } +} diff --git a/tests/sv/advanced/uart_counter.expect b/tests/sv/advanced/uart_counter.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/advanced/uart_counter.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/basic/all_comparisons.cm b/tests/sv/basic/all_comparisons.cm new file mode 100644 index 00000000..08d88932 --- /dev/null +++ b/tests/sv/basic/all_comparisons.cm @@ -0,0 +1,21 @@ +//! platform: sv + +// 比較演算子テスト: ==, !=, <, <=, >, >= の全組み合わせ + +#[input] uint a = 0; +#[input] uint b = 0; +#[output] bool eq = 0; +#[output] bool ne = 0; +#[output] bool lt = 0; +#[output] bool le = 0; +#[output] bool gt = 0; +#[output] bool ge = 0; + +void compare() { + eq = (a == b); + ne = (a != b); + lt = (a < b); + le = (a <= b); + gt = (a > b); + ge = (a >= b); +} diff --git a/tests/sv/basic/all_comparisons.expect b/tests/sv/basic/all_comparisons.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/basic/all_comparisons.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/basic/all_operators.cm b/tests/sv/basic/all_operators.cm new file mode 100644 index 00000000..c2d85770 --- /dev/null +++ b/tests/sv/basic/all_operators.cm @@ -0,0 +1,27 @@ +//! platform: sv + +// 複合演算テスト: 算術+ビット演算の組み合わせ + +#[input] uint a = 0; +#[input] uint b = 0; +#[output] uint add_res = 0; +#[output] uint sub_res = 0; +#[output] uint mul_res = 0; +#[output] uint and_res = 0; +#[output] uint or_res = 0; +#[output] uint xor_res = 0; +#[output] uint shl_res = 0; +#[output] uint shr_res = 0; +#[output] uint not_res = 0; + +void compute() { + add_res = a + b; + sub_res = a - b; + mul_res = a * b; + and_res = a & b; + or_res = a | b; + xor_res = a ^ b; + shl_res = a << 2; + shr_res = b >> 1; + not_res = ~a; +} diff --git a/tests/sv/basic/all_operators.expect b/tests/sv/basic/all_operators.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/basic/all_operators.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/basic/assign_wire.cm b/tests/sv/basic/assign_wire.cm new file mode 100644 index 00000000..742b92d2 --- /dev/null +++ b/tests/sv/basic/assign_wire.cm @@ -0,0 +1,9 @@ +//! platform: sv + +// assign 文テスト: 連続代入(定数式) + +#[input] bool sel = false; +#[input] uint a = 0; +#[input] uint b = 0; + +assign uint result = 42; diff --git a/tests/sv/basic/assign_wire.expect b/tests/sv/basic/assign_wire.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/basic/assign_wire.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/basic/bit_width.cm b/tests/sv/basic/bit_width.cm new file mode 100644 index 00000000..d8c9b71b --- /dev/null +++ b/tests/sv/basic/bit_width.cm @@ -0,0 +1,21 @@ +//! platform: sv + +// bit[N] カスタムビット幅テスト +// bit[4] → logic [3:0], bit[12] → logic [11:0] + +#[input] bool enable = false; +#[input] uint data = 0; +#[output] bit[4] nibble = 0; +#[output] bit[12] address = 0; + +bit[26] counter = 0; + +always_comb void logic_process() { + if (enable) { + nibble = nibble; + address = address; + } else { + nibble = nibble; + address = address; + } +} diff --git a/tests/sv/basic/bit_width.expect b/tests/sv/basic/bit_width.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/basic/bit_width.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/basic/bool_logic.cm b/tests/sv/basic/bool_logic.cm new file mode 100644 index 00000000..86c093bc --- /dev/null +++ b/tests/sv/basic/bool_logic.cm @@ -0,0 +1,15 @@ +//! platform: sv + +// bool入出力 + 論理演算テスト + +#[input] bool a = false; +#[input] bool b = false; +#[output] bool and_out = false; +#[output] bool or_out = false; +#[output] bool not_a = false; + +void logic_ops() { + and_out = a && b; + or_out = a || b; + not_a = !a; +} diff --git a/tests/sv/basic/bool_logic.expect b/tests/sv/basic/bool_logic.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/basic/bool_logic.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/basic/increment.cm b/tests/sv/basic/increment.cm new file mode 100644 index 00000000..352b19d6 --- /dev/null +++ b/tests/sv/basic/increment.cm @@ -0,0 +1,10 @@ +//! platform: sv + +// increment ++ 展開テスト: count++ → count = count + 1 + +#[input] posedge clk; +#[output] uint count = 0; + +always void ticker(posedge clk) { + count++; +} diff --git a/tests/sv/basic/increment.expect b/tests/sv/basic/increment.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/basic/increment.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/basic/inout_port.cm b/tests/sv/basic/inout_port.cm new file mode 100644 index 00000000..3da6178a --- /dev/null +++ b/tests/sv/basic/inout_port.cm @@ -0,0 +1,16 @@ +//! platform: sv + +// inout 双方向ポートテスト + +#[input] bool dir = false; +#[input] uint data_in = 0; +#[inout] uint bus; +#[output] uint data_out = 0; + +always_comb void bus_logic() { + if (dir) { + data_out = data_in; + } else { + data_out = data_in; + } +} diff --git a/tests/sv/basic/inout_port.expect b/tests/sv/basic/inout_port.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/basic/inout_port.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/basic/internal_reg.cm b/tests/sv/basic/internal_reg.cm new file mode 100644 index 00000000..999f9813 --- /dev/null +++ b/tests/sv/basic/internal_reg.cm @@ -0,0 +1,23 @@ +//! platform: sv + +// 内部レジスタ宣言テスト: 属性なしのグローバル変数が内部reg/wireとして宣言される + +#[input] bool clk = 0; +#[input] bool rst = 0; +#[output] uint result = 0; + +// 属性なし → 内部レジスタ +uint stage1 = 0; +uint stage2 = 0; + +always void pipeline(posedge clk) { + if (rst) { + stage1 = 0; + stage2 = 0; + result = 0; + } else { + result = stage2; + stage2 = stage1; + stage1 = stage1 + 1; + } +} diff --git a/tests/sv/basic/internal_reg.expect b/tests/sv/basic/internal_reg.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/basic/internal_reg.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/basic/nested_ternary.cm b/tests/sv/basic/nested_ternary.cm new file mode 100644 index 00000000..8819f80f --- /dev/null +++ b/tests/sv/basic/nested_ternary.cm @@ -0,0 +1,14 @@ +//! platform: sv + +// 三項演算子 ネストテスト + +#[input] utiny sel = 0; +#[input] uint a = 0; +#[input] uint b = 0; +#[input] uint c = 0; +#[input] uint d = 0; +#[output] uint result = 0; + +void mux4() { + result = (sel == 0) ? a : (sel == 1) ? b : (sel == 2) ? c : d; +} diff --git a/tests/sv/basic/nested_ternary.expect b/tests/sv/basic/nested_ternary.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/basic/nested_ternary.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/basic/signed_types.cm b/tests/sv/basic/signed_types.cm new file mode 100644 index 00000000..bd3c1ac4 --- /dev/null +++ b/tests/sv/basic/signed_types.cm @@ -0,0 +1,19 @@ +//! platform: sv + +// signed 型の全幅テスト: tiny, short, int, long のSV出力確認 + +#[input] tiny a_tiny = 0; +#[input] short a_short = 0; +#[input] int a_int = 0; +#[input] long a_long = 0; +#[output] tiny r_tiny = 0; +#[output] short r_short = 0; +#[output] int r_int = 0; +#[output] long r_long = 0; + +void compute() { + r_tiny = a_tiny + 1; + r_short = a_short + 1; + r_int = a_int + 1; + r_long = a_long + 1; +} diff --git a/tests/sv/basic/signed_types.expect b/tests/sv/basic/signed_types.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/basic/signed_types.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/basic/unsigned_types.cm b/tests/sv/basic/unsigned_types.cm new file mode 100644 index 00000000..14ee1024 --- /dev/null +++ b/tests/sv/basic/unsigned_types.cm @@ -0,0 +1,19 @@ +//! platform: sv + +// unsigned 全幅テスト: utiny, ushort, uint, ulong のSV出力確認 + +#[input] utiny a_utiny = 0; +#[input] ushort a_ushort = 0; +#[input] uint a_uint = 0; +#[input] ulong a_ulong = 0; +#[output] utiny r_utiny = 0; +#[output] ushort r_ushort = 0; +#[output] uint r_uint = 0; +#[output] ulong r_ulong = 0; + +void compute() { + r_utiny = a_utiny + 1; + r_ushort = a_ushort + 1; + r_uint = a_uint + 1; + r_ulong = a_ulong + 1; +} diff --git a/tests/sv/basic/unsigned_types.expect b/tests/sv/basic/unsigned_types.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/basic/unsigned_types.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/control/compound_conditions.cm b/tests/sv/control/compound_conditions.cm new file mode 100644 index 00000000..0f51d83b --- /dev/null +++ b/tests/sv/control/compound_conditions.cm @@ -0,0 +1,17 @@ +//! platform: sv + +// 複合条件テスト: && と || の組み合わせ + +#[input] bool a = 0; +#[input] bool b = 0; +#[input] bool c = 0; +#[input] bool d = 0; +#[output] bool r1 = 0; +#[output] bool r2 = 0; +#[output] bool r3 = 0; + +void compound() { + r1 = a && b; + r2 = c || d; + r3 = (a && b) || (c && d); +} diff --git a/tests/sv/control/compound_conditions.expect b/tests/sv/control/compound_conditions.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/control/compound_conditions.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/control/deep_if_else.cm b/tests/sv/control/deep_if_else.cm new file mode 100644 index 00000000..4b59693a --- /dev/null +++ b/tests/sv/control/deep_if_else.cm @@ -0,0 +1,38 @@ +//! platform: sv + +// 深いネストif/elseテスト: 優先度エンコーダ風 + +#[input] utiny req = 0; +#[output] utiny grant = 0; +#[output] bool valid = false; + +void encode() { + if ((req & 128) != 0) { + grant = 7; + valid = true; + } else if ((req & 64) != 0) { + grant = 6; + valid = true; + } else if ((req & 32) != 0) { + grant = 5; + valid = true; + } else if ((req & 16) != 0) { + grant = 4; + valid = true; + } else if ((req & 8) != 0) { + grant = 3; + valid = true; + } else if ((req & 4) != 0) { + grant = 2; + valid = true; + } else if ((req & 2) != 0) { + grant = 1; + valid = true; + } else if ((req & 1) != 0) { + grant = 0; + valid = true; + } else { + grant = 0; + valid = false; + } +} diff --git a/tests/sv/control/deep_if_else.expect b/tests/sv/control/deep_if_else.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/control/deep_if_else.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/control/for_loop.cm b/tests/sv/control/for_loop.cm new file mode 100644 index 00000000..3d7217b1 --- /dev/null +++ b/tests/sv/control/for_loop.cm @@ -0,0 +1,15 @@ +//! platform: sv + +// for ループテスト +// 注意: SV generate for は未実装だがパーサーはfor文をサポート + +#[input] posedge clk; +#[output] uint sum = 0; + +always void accumulate(posedge clk) { + uint total = 0; + for (uint i = 0; i < 4; i = i + 1) { + total = total + i; + } + sum = total; +} diff --git a/tests/sv/control/for_loop.expect b/tests/sv/control/for_loop.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/control/for_loop.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/control/switch_case.cm b/tests/sv/control/switch_case.cm new file mode 100644 index 00000000..eb17d0ba --- /dev/null +++ b/tests/sv/control/switch_case.cm @@ -0,0 +1,29 @@ +//! platform: sv + +// switch → case/endcase テスト + +#[input] bool clk = false; +#[input] bool rst_n = true; +#[input] uint sel = 0; +#[output] uint out = 0; + +always void mux(posedge clk, negedge rst_n) { + if (rst_n == false) { + out = 0; + } else { + switch (sel) { + case(0) { + out = 10; + } + case(1) { + out = 20; + } + case(2) { + out = 30; + } + else { + out = 0; + } + } + } +} diff --git a/tests/sv/control/switch_case.expect b/tests/sv/control/switch_case.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/control/switch_case.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/tests/sv/control/switch_fsm.cm b/tests/sv/control/switch_fsm.cm new file mode 100644 index 00000000..c50d8e1e --- /dev/null +++ b/tests/sv/control/switch_fsm.cm @@ -0,0 +1,37 @@ +//! platform: sv + +// switch + 多段FSMテスト + +#[input] bool clk = false; +#[input] bool rst_n = true; +#[input] bool start = false; +#[output] utiny state = 0; +#[output] bool done = false; + +always void fsm(posedge clk, negedge rst_n) { + if (rst_n == false) { + state = 0; + done = false; + } else { + switch (state) { + case(0) { + if (start) { + state = 1; + } + } + case(1) { + state = 2; + } + case(2) { + state = 3; + } + case(3) { + done = true; + state = 0; + } + else { + state = 0; + } + } + } +} diff --git a/tests/sv/control/switch_fsm.expect b/tests/sv/control/switch_fsm.expect new file mode 100644 index 00000000..8a80bfbc --- /dev/null +++ b/tests/sv/control/switch_fsm.expect @@ -0,0 +1 @@ +COMPILE_OK diff --git a/vscode-extension/package.json b/vscode-extension/package.json index 7cbbd7cd..1bbe59d0 100644 --- a/vscode-extension/package.json +++ b/vscode-extension/package.json @@ -2,7 +2,7 @@ "name": "cm-language", "displayName": "Cm Language Support", "description": "Syntax highlighting and language support for the Cm programming language", - "version": "0.15.0", + "version": "0.15.1", "publisher": "cm-lang", "engines": { "vscode": "^1.80.0" From e9ee9a10b9642a15e70e3b53a12f4d55c624d5ce Mon Sep 17 00:00:00 2001 From: shadowlink0122 Date: Wed, 11 Mar 2026 01:51:39 +0900 Subject: [PATCH 02/12] =?UTF-8?q?extern=20struct=20=E5=AE=9F=E8=A3=85:=20?= =?UTF-8?q?=E5=A4=96=E9=83=A8=E3=83=8F=E3=83=BC=E3=83=89=E3=82=A6=E3=82=A7?= =?UTF-8?q?=E3=82=A2=E3=83=A2=E3=82=B8=E3=83=A5=E3=83=BC=E3=83=AB=E3=81=AE?= =?UTF-8?q?=E3=82=A4=E3=83=B3=E3=82=B9=E3=82=BF=E3=83=B3=E3=82=B9=E5=8C=96?= =?UTF-8?q?=E3=82=B5=E3=83=9D=E3=83=BC=E3=83=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - パーサー: extern struct 構文, フィールド属性パース (#[sv::param], #[output] 等) - パーサー: extern struct フィールドのデフォルト値パース (= expr) - パーサー: SVプラットフォームで初期値なし構造体型グローバル変数宣言を許可 - HIR/MIR: is_extern フラグ, フィールド属性, デフォルト値の伝播チェーン - SV Codegen: extern struct typedef 出力抑制 - SV Codegen: モジュールインスタンス化文生成 (パラメータ + ポート接続) - SV Codegen: extern struct 型のレジスタ宣言重複を解消 - テスト: extern_instance テストケース追加 - SVテスト 61/61 PASS, インタプリタテスト 349/372 PASS (既知18 FAIL) --- src/codegen/sv/codegen.cpp | 115 +++++++++++++++++++++ src/codegen/sv/codegen.hpp | 1 + src/frontend/ast/decl.hpp | 1 + src/frontend/lexer/lexer.hpp | 3 + src/frontend/parser/parser.hpp | 8 +- src/frontend/parser/parser_decl.cpp | 13 ++- src/frontend/parser/parser_expr.cpp | 4 +- src/frontend/parser/parser_module.cpp | 12 ++- src/hir/lowering/decl.cpp | 25 ++++- src/hir/nodes.hpp | 3 + src/main.cpp | 4 +- src/mir/lowering/base.cpp | 5 + src/mir/lowering/monomorphization_impl.cpp | 1 + src/mir/nodes.hpp | 6 ++ src/module/resolver.cpp | 2 +- tests/sv/advanced/extern_instance.cm | 27 +++++ tests/sv/advanced/extern_instance.error | 1 + 17 files changed, 220 insertions(+), 11 deletions(-) create mode 100644 tests/sv/advanced/extern_instance.cm create mode 100644 tests/sv/advanced/extern_instance.error diff --git a/src/codegen/sv/codegen.cpp b/src/codegen/sv/codegen.cpp index a71154d7..96d9235a 100644 --- a/src/codegen/sv/codegen.cpp +++ b/src/codegen/sv/codegen.cpp @@ -248,6 +248,17 @@ void SVCodeGen::emitModule(const SVModule& mod) { emitLine(stmt); } + // extern struct インスタンス化文 + for (const auto& inst : mod.instance_blocks) { + append_line(""); + // 複数行のインスタンス化文を行ごとに出力 + std::istringstream iss(inst); + std::string line; + while (std::getline(iss, line)) { + emitLine(line); + } + } + // function/task ブロック for (const auto& fn : mod.function_blocks) { append_line(""); @@ -662,6 +673,17 @@ void SVCodeGen::analyzeFunction(const mir::MirFunction& func, SVModule& mod) { // ポートと名前が衝突する場合はスキップ if (port_names.count(name)) continue; + // extern struct インスタンスと同名の変数はスキップ + bool is_instance_var = false; + for (const auto& inst : mod.instance_blocks) { + if (inst.find(" " + name + " ") != std::string::npos || + inst.find(" " + name + ";") != std::string::npos) { + is_instance_var = true; + break; + } + } + if (is_instance_var) + continue; // parameter宣言と名前が衝突する場合はスキップ bool is_param_var = false; for (const auto& param : mod.parameters) { @@ -1717,6 +1739,97 @@ void SVCodeGen::analyzeMIR(const mir::MirProgram& program) { if (!gv) continue; + // extern struct インスタンスの検出(型名ベース) + if (gv->type) { + const mir::MirStruct* extern_st = nullptr; + for (const auto& st : program.structs) { + if (st && st->name == gv->type->name && st->is_extern) { + extern_st = st.get(); + break; + } + } + if (extern_st) { + // インスタンス化文を生成 + std::string inst; + inst += extern_st->name; + + // パラメータ部(#[sv::param]属性) + std::vector params; + std::vector ports; + + for (const auto& field : extern_st->fields) { + bool is_sv_param = false; + bool is_port = false; + for (const auto& attr : field.attributes) { + if (attr == "sv::param") is_sv_param = true; + if (attr == "input" || attr == "output" || attr == "inout") is_port = true; + } + + if (is_sv_param) { + // デフォルト値: フィールドの default_value_str → struct_field_inits → "0" + std::string val = "0"; + if (!field.default_value_str.empty()) { + val = field.default_value_str; + } else { + for (const auto& [fname, fconst] : gv->struct_field_inits) { + if (fname == field.name) { + if (auto* ival = std::get_if(&fconst.value)) { + val = std::to_string(*ival); + } else if (auto* bval = std::get_if(&fconst.value)) { + val = *bval ? "1" : "0"; + } + break; + } + } + } + params.push_back("." + field.name + "(" + val + ")"); + } else if (is_port) { + // ポート接続: フィールドの default_value_str → struct_field_inits → フィールド名 + std::string sig = field.name; + if (!field.default_value_str.empty()) { + sig = field.default_value_str; + } else { + for (const auto& [fname, fconst] : gv->struct_field_inits) { + if (fname == field.name) { + if (auto* sval = std::get_if(&fconst.value)) { + sig = *sval; + } + break; + } + } + } + ports.push_back("." + field.name + "(" + sig + ")"); + } + } + + if (!params.empty()) { + inst += " #(\n"; + for (size_t i = 0; i < params.size(); ++i) { + inst += " " + params[i]; + if (i + 1 < params.size()) inst += ","; + inst += "\n"; + } + inst += " )"; + } + + inst += " " + gv->name; + + if (!ports.empty()) { + inst += " (\n"; + for (size_t i = 0; i < ports.size(); ++i) { + inst += " " + ports[i]; + if (i + 1 < ports.size()) inst += ","; + inst += "\n"; + } + inst += " )"; + } + + inst += ";"; + default_mod.instance_blocks.push_back(inst); + continue; + } + } + // 属性からポート方向を判定 bool is_input = false; bool is_output = false; @@ -1870,8 +1983,10 @@ void SVCodeGen::analyzeMIR(const mir::MirProgram& program) { } // struct → typedef struct packed 出力(#[sv::packed]属性付きのみ) + // extern struct はモジュール定義なので除外 for (const auto& st : program.structs) { if (!st) continue; + if (st->is_extern) continue; // extern struct はtypedef出力しない // TODO: sv::packed属性チェック(現状は全structをpacked出力) std::ostringstream ss; ss << "typedef struct packed {\n"; diff --git a/src/codegen/sv/codegen.hpp b/src/codegen/sv/codegen.hpp index 1a1047a6..6387ed9a 100644 --- a/src/codegen/sv/codegen.hpp +++ b/src/codegen/sv/codegen.hpp @@ -43,6 +43,7 @@ struct SVModule { std::vector function_blocks; // function automatic ブロック std::vector wire_declarations; // 内部ワイヤ宣言 std::vector reg_declarations; // 内部レジスタ宣言 + std::vector instance_blocks; // extern struct インスタンス化文 }; // SystemVerilog コードジェネレータ diff --git a/src/frontend/ast/decl.hpp b/src/frontend/ast/decl.hpp index 7b7bb252..de020fec 100644 --- a/src/frontend/ast/decl.hpp +++ b/src/frontend/ast/decl.hpp @@ -165,6 +165,7 @@ struct StructDecl { std::vector fields; Visibility visibility = Visibility::Private; std::vector attributes; + bool is_extern = false; // extern struct(外部ハードウェアモジュール等) // with キーワードで自動実装するinterface std::vector auto_impls; diff --git a/src/frontend/lexer/lexer.hpp b/src/frontend/lexer/lexer.hpp index 3fbb29fc..e3101a64 100644 --- a/src/frontend/lexer/lexer.hpp +++ b/src/frontend/lexer/lexer.hpp @@ -24,6 +24,9 @@ class Lexer { // トークン化(メインエントリ) std::vector tokenize(); + // SVプラットフォームかどうかを返す + bool is_sv() const { return platform_ == LexerPlatform::SV; } + private: // 次のトークンを取得 Token next_token(); diff --git a/src/frontend/parser/parser.hpp b/src/frontend/parser/parser.hpp index 638daca8..5f8f5ff4 100644 --- a/src/frontend/parser/parser.hpp +++ b/src/frontend/parser/parser.hpp @@ -21,12 +21,13 @@ using DiagKind = Severity; // ============================================================ class Parser { public: - Parser(std::vector tokens) + Parser(std::vector tokens, bool is_sv_platform = false) : tokens_(std::move(tokens)), pos_(0), last_error_line_(0), parse_depth_(0), - max_parse_depth_(0) {} + max_parse_depth_(0), + is_sv_platform_(is_sv_platform) {} // プログラム全体を解析(parser_decl.cppで実装) ast::Program parse(); @@ -50,7 +51,7 @@ class Parser { std::vector attributes = {}, bool is_async = false); std::vector parse_params(); - ast::DeclPtr parse_struct(bool is_export, std::vector attributes = {}); + ast::DeclPtr parse_struct(bool is_export, std::vector attributes = {}, bool is_extern = false); std::optional parse_operator_kind(); ast::DeclPtr parse_interface(bool is_export, std::vector attributes = {}); ast::DeclPtr parse_impl(std::vector attributes = {}); @@ -190,6 +191,7 @@ class Parser { false; // 演算子戻り値型パース中フラグ(*&の型サフィックス抑制) int parse_depth_ = 0; // 再帰深度カウンター int max_parse_depth_ = 0; // 最大再帰深度記録 + bool is_sv_platform_ = false; // SVプラットフォームフラグ }; } // namespace cm diff --git a/src/frontend/parser/parser_decl.cpp b/src/frontend/parser/parser_decl.cpp index 8d584a46..527f4042 100644 --- a/src/frontend/parser/parser_decl.cpp +++ b/src/frontend/parser/parser_decl.cpp @@ -429,7 +429,7 @@ std::vector Parser::parse_params() { } // 構造体 -ast::DeclPtr Parser::parse_struct(bool is_export, std::vector attributes) { +ast::DeclPtr Parser::parse_struct(bool is_export, std::vector attributes, bool is_extern) { uint32_t start_pos = current().start; debug::par::log(debug::par::Id::StructDef, "", debug::Level::Trace); @@ -488,6 +488,11 @@ ast::DeclPtr Parser::parse_struct(bool is_export, std::vector Date: Wed, 11 Mar 2026 02:35:22 +0900 Subject: [PATCH 04/12] =?UTF-8?q?=E4=BF=AE=E6=AD=A3:=20=E3=82=B0=E3=83=AD?= =?UTF-8?q?=E3=83=BC=E3=83=90=E3=83=AB=E6=96=87=E5=AD=97=E5=88=97=E5=88=9D?= =?UTF-8?q?=E6=9C=9F=E5=8C=96=E3=81=AECreateGlobalStringPtr=E3=83=8F?= =?UTF-8?q?=E3=83=B3=E3=82=B0=E3=82=92=E8=A7=A3=E6=B6=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MIRToLLVM::convert のグローバル変数初期化で builder->CreateGlobalStringPtr を 使用していたが、IRBuilderにインサートポイント(BasicBlock)が設定されていない 状態での呼び出しがLLVM内部で無限ループを引き起こしていた。 修正: ConstantDataArray::getString + ConstantExpr::getInBoundsGetElementPtr で IRBuilderを介さずにグローバル文字列定数を直接作成するよう変更。 - macro string テスト (typed_macro, generic_with_macro) の完全復旧 - インタプリタテスト: 367/372 PASS, 0 FAIL, 5 SKIP - SVテスト: 61/61 PASS --- src/codegen/llvm/core/mir_to_llvm.cpp | 35 +++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/src/codegen/llvm/core/mir_to_llvm.cpp b/src/codegen/llvm/core/mir_to_llvm.cpp index 8a87b77e..a3de6666 100644 --- a/src/codegen/llvm/core/mir_to_llvm.cpp +++ b/src/codegen/llvm/core/mir_to_llvm.cpp @@ -760,11 +760,24 @@ void MIRToLLVM::convert(const mir::MirProgram& program) { // 初期値の決定 llvm::Constant* initialValue = nullptr; if (gv->init_value) { - // 文字列型の場合 + // 文字列型の場合: IRBuilderなしでグローバル文字列定数を作成 if (std::holds_alternative(gv->init_value->value)) { auto& str = std::get(gv->init_value->value); - auto strConst = builder->CreateGlobalStringPtr(str, gv->name + ".str"); - initialValue = llvm::cast(strConst); + // 文字列データをグローバル定数として配置 + auto strConstant = llvm::ConstantDataArray::getString(ctx.getContext(), str, true); + auto strGlobal = new llvm::GlobalVariable( + *module, strConstant->getType(), true, + llvm::GlobalValue::PrivateLinkage, strConstant, gv->name + ".str"); + strGlobal->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); + // i8* へのポインタを取得 + initialValue = llvm::ConstantExpr::getBitCast( + llvm::ConstantExpr::getInBoundsGetElementPtr( + strConstant->getType(), strGlobal, + llvm::ArrayRef{ + llvm::ConstantInt::get(ctx.getI64Type(), 0), + llvm::ConstantInt::get(ctx.getI64Type(), 0) + }), + ctx.getPtrType()); } // 整数型の場合 else if (std::holds_alternative(gv->init_value->value)) { @@ -1088,10 +1101,22 @@ void MIRToLLVM::convert(const mir::ModuleProgram& module) { llvm::Constant* initialValue = nullptr; if (gv->init_value) { + // 文字列型の場合: IRBuilderなしでグローバル文字列定数を作成 if (std::holds_alternative(gv->init_value->value)) { auto& str = std::get(gv->init_value->value); - auto strConst = builder->CreateGlobalStringPtr(str, gv->name + ".str"); - initialValue = llvm::cast(strConst); + auto strConstant = llvm::ConstantDataArray::getString(ctx.getContext(), str, true); + auto strGlobal = new llvm::GlobalVariable( + *this->module, strConstant->getType(), true, + llvm::GlobalValue::PrivateLinkage, strConstant, gv->name + ".str"); + strGlobal->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); + initialValue = llvm::ConstantExpr::getBitCast( + llvm::ConstantExpr::getInBoundsGetElementPtr( + strConstant->getType(), strGlobal, + llvm::ArrayRef{ + llvm::ConstantInt::get(ctx.getI64Type(), 0), + llvm::ConstantInt::get(ctx.getI64Type(), 0) + }), + ctx.getPtrType()); } else if (std::holds_alternative(gv->init_value->value)) { initialValue = llvm::ConstantInt::get(llvmType, std::get(gv->init_value->value)); From 1708f29c2de8503eaf9399616aed8dbee4b32333 Mon Sep 17 00:00:00 2001 From: shadowlink0122 Date: Wed, 11 Mar 2026 06:27:09 +0900 Subject: [PATCH 05/12] =?UTF-8?q?=E4=BF=AE=E6=AD=A3:=20MIR=E2=86=92LLVM?= =?UTF-8?q?=E5=A4=89=E6=8F=9B=E3=81=AB=E5=88=B0=E9=81=94=E5=8F=AF=E8=83=BD?= =?UTF-8?q?=E6=80=A7=E5=88=86=E6=9E=90=E3=82=92=E8=BF=BD=E5=8A=A0=E3=81=97?= =?UTF-8?q?=E5=88=B0=E9=81=94=E4=B8=8D=E8=83=BD=E3=83=96=E3=83=AD=E3=83=83?= =?UTF-8?q?=E3=82=AF=E3=82=92=E3=82=B9=E3=82=AD=E3=83=83=E3=83=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MIR生成時に全関数の末尾に到達不能な 'return 0' ブロックが生成される。 LLVM O3最適化がこれを unreachable → ud2 (x86_64) に変換し、 Ubuntu環境でSIGILL (exit code 132)を引き起こしていた。 修正: convertFunctionにエントリブロックからのBFS到達可能性分析を追加。 Goto/SwitchInt/Callの遷移先を辿り到達可能ブロックを収集し、 到達不能ブロックのLLVMブロック作成とコード変換をスキップ。 - LLVM O3テスト: 403/411 PASS, 0 FAIL (Ubuntu x86_64 SIGILL解消) - インタプリタテスト: 回帰なし --- src/codegen/llvm/core/mir_to_llvm.cpp | 67 +++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 5 deletions(-) diff --git a/src/codegen/llvm/core/mir_to_llvm.cpp b/src/codegen/llvm/core/mir_to_llvm.cpp index a3de6666..f56d988d 100644 --- a/src/codegen/llvm/core/mir_to_llvm.cpp +++ b/src/codegen/llvm/core/mir_to_llvm.cpp @@ -1723,11 +1723,70 @@ void MIRToLLVM::convertFunction(const mir::MirFunction& func) { } } - // 基本ブロック作成 + // 到達可能性分析: エントリブロックから到達可能なブロックのみを変換 + // 到達不能ブロック(例: デフォルトの return 0)がLLVM O3で + // unreachable → ud2 (x86_64 SIGILL) に最適化される問題を防止 + std::unordered_set reachableBlocks; + { + std::queue worklist; + size_t entry = func.entry_block; + if (entry < func.basic_blocks.size() && func.basic_blocks[entry]) { + worklist.push(entry); + reachableBlocks.insert(entry); + } else if (!func.basic_blocks.empty() && func.basic_blocks[0]) { + worklist.push(0); + reachableBlocks.insert(0); + } + while (!worklist.empty()) { + size_t current = worklist.front(); + worklist.pop(); + const auto& bb = func.basic_blocks[current]; + if (!bb) continue; + // ターミネーターの遷移先を収集 + if (bb->terminator) { + auto addSuccessor = [&](size_t target) { + if (target < func.basic_blocks.size() && func.basic_blocks[target] && + reachableBlocks.insert(target).second) { + worklist.push(target); + } + }; + switch (bb->terminator->kind) { + case mir::MirTerminator::Goto: { + auto& data = std::get(bb->terminator->data); + addSuccessor(data.target); + break; + } + case mir::MirTerminator::SwitchInt: { + auto& data = std::get(bb->terminator->data); + for (auto& [_, target] : data.targets) { + addSuccessor(target); + } + addSuccessor(data.otherwise); + break; + } + case mir::MirTerminator::Call: { + auto& data = std::get(bb->terminator->data); + addSuccessor(data.success); + break; + } + case mir::MirTerminator::Return: + // 遷移先なし + break; + default: + break; + } + } + } + } + + // 基本ブロック作成(到達可能なブロックのみ) for (size_t i = 0; i < func.basic_blocks.size(); ++i) { // DCEで削除されたブロックはスキップ if (!func.basic_blocks[i]) continue; + // 到達不能ブロックはスキップ + if (reachableBlocks.count(i) == 0) + continue; auto bbName = "bb" + std::to_string(i); blocks[i] = llvm::BasicBlock::Create(ctx.getContext(), bbName, currentFunction); } @@ -1747,10 +1806,8 @@ void MIRToLLVM::convertFunction(const mir::MirFunction& func) { // func.basic_blocks.size() // << " blocks\n"; for (size_t i = 0; i < func.basic_blocks.size(); ++i) { - // DCEで削除されたブロックはスキップ - if (!func.basic_blocks[i]) { - // std::cerr << "[MIR2LLVM] Block " << i << " is null (DCE removed), - // skipping\n"; + // DCEで削除されたブロック / 到達不能ブロックはスキップ + if (!func.basic_blocks[i] || reachableBlocks.count(i) == 0) { continue; } From ce7959edee0e06679d0b8bca2e38539de86642e0 Mon Sep 17 00:00:00 2001 From: shadowlink0122 Date: Wed, 11 Mar 2026 06:28:10 +0900 Subject: [PATCH 06/12] =?UTF-8?q?=E4=BF=AE=E6=AD=A3:=20wasmtime=20CI?= =?UTF-8?q?=E3=82=BB=E3=83=83=E3=83=88=E3=82=A2=E3=83=83=E3=83=97=E3=82=92?= =?UTF-8?q?curl=E3=82=A4=E3=83=B3=E3=82=B9=E3=83=88=E3=83=BC=E3=83=AB?= =?UTF-8?q?=E3=81=AB=E5=88=87=E3=82=8A=E6=9B=BF=E3=81=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bytecodealliance/actions/wasmtime/setup@v1 がGitHub APIレート制限で HTMLエラーページを受け取りCIが失敗する問題を修正。 Wasmtime公式インストールスクリプト(curl https://wasmtime.dev/install.sh) による直接インストールに切り替え。 --- .github/workflows/ci.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b875d3e3..34153dff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -289,9 +289,11 @@ jobs: - name: Install wasmtime if: matrix.config.backend == 'llvm-wasm' - uses: bytecodealliance/actions/wasmtime/setup@v1 - with: - version: "latest" + run: | + # bytecodealliance/actions/wasmtime/setup@v1 はGitHub APIレート制限で + # HTMLエラーを返すことがあるため、直接インストールスクリプトを使用 + curl https://wasmtime.dev/install.sh -sSf | bash + echo "$HOME/.wasmtime/bin" >> $GITHUB_PATH - name: Install SV tools (for SV backend tests) if: matrix.config.backend == 'sv' From cae567b4b0ea6f843684d76c22681fc71c24ecae Mon Sep 17 00:00:00 2001 From: shadowlink0122 Date: Wed, 11 Mar 2026 21:10:56 +0900 Subject: [PATCH 07/12] =?UTF-8?q?=E3=83=89=E3=82=AD=E3=83=A5=E3=83=A1?= =?UTF-8?q?=E3=83=B3=E3=83=88:=20SV=20=E3=83=90=E3=83=83=E3=82=AF=E3=82=A8?= =?UTF-8?q?=E3=83=B3=E3=83=89=20=E3=83=81=E3=83=A5=E3=83=BC=E3=83=88?= =?UTF-8?q?=E3=83=AA=E3=82=A2=E3=83=AB=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit docs/v0.15.1/sv_tutorial.md: 包括的なSVバックエンドチュートリアル - 構文・型マッピング・ポート宣言 - ロジックブロック (always_ff/always_comb) の4パターン - 演算子マッピング・定数リテラル・ビット幅付与 - Cm独自の暗黙的変換 (代入方式自動決定・論理否定→ビット反転・ clk/rst自動追加・MIR一時変数展開・else if正規化 等) - SV属性一覧・トークン一覧・予約語 - LED点滅の完全な例 (Cm → SV 出力) --- docs/v0.15.1/sv_tutorial.md | 778 ++++++++++++++++++++++++++++++++++++ 1 file changed, 778 insertions(+) create mode 100644 docs/v0.15.1/sv_tutorial.md diff --git a/docs/v0.15.1/sv_tutorial.md b/docs/v0.15.1/sv_tutorial.md new file mode 100644 index 00000000..54e673f6 --- /dev/null +++ b/docs/v0.15.1/sv_tutorial.md @@ -0,0 +1,778 @@ +# Cm SystemVerilog チュートリアル + +Cm コンパイラの SV バックエンドを使用して、FPGA 向けの SystemVerilog コードを生成するための包括的なガイドです。 + +--- + +## 目次 + +1. [はじめに](#1-はじめに) +2. [最初の回路: LED 点滅](#2-最初の回路-led-点滅) +3. [プラットフォームディレクティブ](#3-プラットフォームディレクティブ) +4. [型システム](#4-型システム) +5. [ポート宣言](#5-ポート宣言) +6. [ロジックブロック](#6-ロジックブロック) +7. [演算子](#7-演算子) +8. [定数リテラルとビット幅](#8-定数リテラルとビット幅) +9. [定数と localparam](#9-定数と-localparam) +10. [制御構文](#10-制御構文) +11. [連接と複製](#11-連接と複製) +12. [列挙型 (FSM)](#12-列挙型-fsm) +13. [SV 属性](#13-sv-属性) +14. [暗黙的変換](#14-暗黙的変換) +15. [コンパイルと検証](#15-コンパイルと検証) +16. [全体例: カウンタ付き LED 点滅](#16-全体例-カウンタ付き-led-点滅) +17. [付録: トークン・キーワード一覧](#17-付録-トークンキーワード一覧) + +--- + +## 1. はじめに + +Cm の SV バックエンドは、Cm の既存構文を活用して **合成可能な SystemVerilog** を生成します。 +ソフトウェア開発者にとって馴染み深い Cm の構文で FPGA 回路を記述でき、 +コンパイラが適切な SV 構文(`always_ff`, `always_comb`, `<=` 代入等)に自動変換します。 + +### 設計哲学 + +- **Cm の構文を最大限活用**: 新しいキーワードは最小限にし、既存の `if/else`, `switch`, `enum` 等をそのまま使用 +- **暗黙的な SV マッピング**: `=` 代入は文脈に応じて `<=` (ノンブロッキング) と `=` (ブロッキング) に自動変換 +- **型安全なハードウェア記述**: 非合成型(`float`, `string`, ポインタ)はコンパイルエラー +- **1 ファイル = 1 モジュール**: ファイル名がモジュール名になる + +--- + +## 2. 最初の回路: LED 点滅 + +```cm +//! platform: sv + +#[input] posedge clk; +#[input] bool rst = false; +#[output] bool led = false; + +uint counter = 0; + +void blink(posedge clk) { + if (rst) { + counter = 0; + led = false; + } else { + if (counter == 49999999) { + counter = 0; + led = !led; + } else { + counter = counter + 1; + } + } +} +``` + +コンパイル: +```bash +cm compile --target=sv blink.cm -o blink.sv +``` + +生成される SV: +```systemverilog +`timescale 1ns / 1ps + +module blink ( + input logic clk, + input logic rst, + output logic led +); + logic [31:0] counter; + + always_ff @(posedge clk) begin + if (rst) begin + counter <= 32'd0; + led <= 1'b0; + end else begin + if (counter == 32'd49999999) begin + counter <= 32'd0; + led <= ~led; + end else begin + counter <= counter + 32'd1; + end + end + end +endmodule +``` + +> **ポイント**: Cm の `=` が自動的に SV の `<=` (ノンブロッキング代入) に変換されています。 +> `!led` も SV の `~led` に変換されています。 + +--- + +## 3. プラットフォームディレクティブ + +SV バックエンドを使用するには、ファイル先頭に **必ず** 以下のディレクティブを記述します: + +```cm +//! platform: sv +``` + +このディレクティブにより: +- `posedge`, `negedge`, `wire`, `reg` 等の SV 固有キーワードが有効化 +- 非合成型(`float`, `string`, ポインタ)に対するバリデーションが有効化 +- `always`, `assign`, `initial` 等の SV 構文が使用可能 + +--- + +## 4. 型システム + +### 4.1 基本型と SV マッピング + +| Cm 型 | SV 出力 | ビット幅 | 用途 | +|-------|---------|---------|------| +| `bool` | `logic` | 1 | フラグ、制御信号 | +| `utiny` | `logic [7:0]` | 8 | 小さなカウンタ、状態 | +| `ushort` | `logic [15:0]` | 16 | アドレス、中間値 | +| `uint` | `logic [31:0]` | 32 | カウンタ、データ | +| `ulong` | `logic [63:0]` | 64 | タイムスタンプ、大規模データ | +| `tiny` | `logic signed [7:0]` | 8 | 符号付き小数値 | +| `short` | `logic signed [15:0]` | 16 | 符号付き中間値 | +| `int` | `logic signed [31:0]` | 32 | 符号付きデータ | +| `long` | `logic signed [63:0]` | 64 | 符号付き大規模データ | + +### 4.2 SV 固有型 + +| Cm 型 | 用途 | SV 出力 | +|-------|------|---------| +| `posedge` | クロック立ち上がりエッジ信号 | `logic` (1-bit) | +| `negedge` | クロック/リセット立ち下がりエッジ信号 | `logic` (1-bit) | +| `wire` | ワイヤ修飾(組み合わせ出力) | `T` のマッピングに準拠 | +| `reg` | レジスタ修飾(順序回路出力) | `T` のマッピングに準拠 | + +### 4.3 カスタムビット幅 + +任意のビット幅を `bit[N]` で指定: + +```cm +#[output] bit[4] nibble; // → output logic [3:0] nibble +#[output] bit[12] address; // → output logic [11:0] address +bit[26] counter; // → logic [25:0] counter +``` + +### 4.4 非合成型 (コンパイルエラー) + +以下の型は SV バックエンドで **コンパイルエラー** になります: + +- `float`, `double` — 浮動小数点 +- `string`, `cstring` — 文字列 +- `*T` (ポインタ), `&T` (参照) + +--- + +## 5. ポート宣言 + +ポートは属性付きグローバル変数で宣言します: + +```cm +// 入力ポート +#[input] posedge clk; // → input logic clk +#[input] bool rst = false; // → input logic rst +#[input] utiny data_in; // → input logic [7:0] data_in + +// 出力ポート +#[output] bool led = false; // → output logic led +#[output] utiny led_array = 0xFF; // → output logic [7:0] led_array +#[output] uint data_out; // → output logic [31:0] data_out + +// 双方向ポート +#[inout] ushort bus; // → inout logic [15:0] bus + +// パラメータ(外部から上書き可能) +#[sv::param] uint WIDTH = 8; // → parameter WIDTH = 32'd8; +``` + +> **初期値**: ポートの初期値(`= false`, `= 0xFF` 等)はポート宣言には反映されず、 +> 内部ロジックのリセット値として使用されます。 + +--- + +## 6. ロジックブロック + +### 6.1 順序回路 (always_ff) + +#### パターン A: `always` + エッジパラメータ (推奨) + +```cm +always void counter(posedge clk) { + count = count + 1; +} +// → always_ff @(posedge clk) begin +// count <= count + 32'd1; +// end +``` + +#### パターン B: 非同期リセット付き(複数エッジ) + +```cm +always void process(posedge clk, negedge rst_n) { + if (rst_n == false) { + count = 0; + } else { + count = count + 1; + } +} +// → always_ff @(posedge clk or negedge rst_n) begin +// if (rst_n == 1'b0) begin +// count <= 32'd0; +// end else begin +// count <= count + 32'd1; +// end +// end +``` + +#### パターン C: `void f(posedge clk)` (後方互換) + +```cm +void blink(posedge clk) { + led = !led; +} +// → always_ff @(posedge clk) begin +// led <= ~led; +// end +``` + +#### パターン D: `async func` (後方互換) + +```cm +async func tick() { + counter = counter + 1; +} +// → always_ff @(posedge clk) begin +// counter <= counter + 32'd1; +// end +``` + +> **注意**: `async func` は暗黙的に `clk` 変数を参照します。 +> `clk` が未宣言の場合、自動的に `input logic clk` が追加されます。 + +### 6.2 組み合わせ回路 (always_comb) + +エッジパラメータなしの関数は `always_comb` に変換されます: + +```cm +always void decode() { + out = 0; // デフォルト値(ラッチ防止) + if (sel) { + out = a; + } else { + out = b; + } +} +// → always_comb begin +// out = 32'd0; +// if (sel) begin out = a; end +// else begin out = b; end +// end +``` + +トリガなし `void f()` / `func f()` も `always_comb` に変換されます(後方互換): + +```cm +void update() { + signal = (counter > 100); +} +// → always_comb begin +// signal = (counter > 32'd100); +// end +``` + +### 6.3 代入の自動変換ルール + +| ブロック種別 | Cm での記述 | SV 出力 | +|------------|-----------|---------| +| `always_ff` (順序回路) | `x = expr;` | `x <= expr;` (ノンブロッキング) | +| `always_comb` (組み合わせ) | `x = expr;` | `x = expr;` (ブロッキング) | + +Cm では常に `=` で記述し、コンパイラが文脈に応じて適切な代入方式を選択します。 + +--- + +## 7. 演算子 + +### 7.1 算術演算子 + +| Cm | SV | 例 | +|----|----|----| +| `+` | `+` | `counter + 1` → `counter + 32'd1` | +| `-` | `-` | `a - b` | +| `*` | `*` | `a * b` | +| `/` | `/` | `a / b` | +| `%` | `%` | `a % 10` | + +### 7.2 ビット演算子 + +| Cm | SV | 例 | +|----|----|----| +| `&` | `&` | `a & 0xFF` | +| `\|` | `\|` | `a \| b` | +| `^` | `^` | `a ^ b` | +| `~` | `~` | `~a` | +| `<<` | `<<` | `a << 2` | +| `>>` | `>>` | `a >> 1` | + +### 7.3 比較/論理演算子 + +| Cm | SV | 備考 | +|----|----|----| +| `==` | `==` | | +| `!=` | `!=` | | +| `<` | `<` | | +| `<=` | `<=` | 比較演算子(代入の `<=` とは異なる) | +| `>` | `>` | | +| `>=` | `>=` | | +| `&&` | `&&` | | +| `\|\|` | `\|\|` | | +| `!` | `~` | **暗黙変換**: 論理否定がビット反転に統合 | + +### 7.4 暗黙的な演算子変換 + +> **重要**: Cm の `!` (論理否定) は SV では `~` (ビット反転) にマッピングされます。 +> SV の `!` は 1 ビット論理否定ですが、現在のバックエンドは多ビット信号にも安全な `~` に統一しています。 + +--- + +## 8. 定数リテラルとビット幅 + +Cm の定数リテラルは、文脈のビット幅に合わせて **自動的にビット幅付きリテラル** に変換されます: + +| Cm リテラル | 文脈の型 | SV 出力 | +|------------|---------|---------| +| `true` | `bool` | `1'b1` | +| `false` | `bool` | `1'b0` | +| `42` | `uint` (32-bit) | `32'd42` | +| `42` | `utiny` (8-bit) | `8'd42` | +| `42` | `int` (符号付き32-bit) | `32'sd42` | +| `-5` | `int` | `-32'sd5` | + +### SV スタイルのリテラル + +Cm は SV スタイルのビット幅指定リテラルもそのまま使用可能: + +```cm +utiny mask = 8'b10101010; // → 8'b10101010 +ushort addr = 16'hFF00; // → 16'hFF00 +``` + +### 数値区切り文字 + +大きな数値にはアンダースコア `_` が使えます: + +```cm +const uint CLK_FREQ = 50_000_000; // → localparam CLK_FREQ = 32'd50000000; +``` + +--- + +## 9. 定数と localparam + +### `const` → `localparam` + +```cm +const uint CLK_FREQ = 27_000_000; +const uint CNT_MAX = CLK_FREQ / 2 - 1; +``` +```systemverilog +localparam CLK_FREQ = 32'd27000000; +localparam CNT_MAX = CLK_FREQ / 2 - 32'd1; +``` + +### `#[sv::param]` → `parameter` + +外部モジュールからオーバーライド可能なパラメータ: + +```cm +#[sv::param] const uint WIDTH = 8; +``` +```systemverilog +parameter WIDTH = 32'd8; +``` + +--- + +## 10. 制御構文 + +### 10.1 if / else if / else + +```cm +if (rst) { + counter = 0; +} else if (enable) { + counter = counter + 1; +} else { + // idle +} +``` +```systemverilog +if (rst) begin + counter <= 32'd0; +end else if (enable) begin + counter <= counter + 32'd1; +end else begin + // idle +end +``` + +### 10.2 switch → case + +```cm +switch (state) { + case 0: { + next_state = 1; + } + case 1: { + next_state = 2; + } + default: { + next_state = 0; + } +} +``` +```systemverilog +case (state) + 32'd0: begin + next_state <= 32'd1; + end + 32'd1: begin + next_state <= 32'd2; + end + default: begin + next_state <= 32'd0; + end +endcase +``` + +--- + +## 11. 連接と複製 + +### 連接 (Concatenation) + +```cm +result = {a, b}; // → result = {a, b}; +wide = {a, b, c}; // → wide = {a, b, c}; +``` + +### 複製 (Replication) + +```cm +replicated = {3{a}}; // → replicated = {3{a}}; +``` + +### ビルトイン関数 + +連接の `{...}` 構文がブロック `{...}` と曖昧になる場合、ビルトイン関数を使用: + +```cm +result = concat(a, b); // → result = {a, b}; +wide = replicate(nibble, 3); // → wide = {3{nibble}}; +``` + +--- + +## 12. 列挙型 (FSM) + +Cm の `enum` は SV の `typedef enum logic` に変換されます。 +ビット幅はバリアント数から自動計算: + +```cm +enum State { + IDLE, + RUN, + DONE, + ERROR +} +``` +```systemverilog +typedef enum logic [1:0] { + IDLE = 2'd0, + RUN = 2'd1, + DONE = 2'd2, + ERROR = 2'd3 +} State; +``` + +FSM での使用例: + +```cm +//! platform: sv + +enum State { IDLE, RUN, DONE } + +#[input] posedge clk; +#[input] bool rst = false; +#[output] uint count = 0; + +always void process(posedge clk) { + if (rst) { + count = 0; + } else { + switch (state) { + case State::IDLE: { state = State::RUN; } + case State::RUN: { count = count + 1; } + default: {} + } + } +} +``` + +--- + +## 13. SV 属性 + +### ポート属性 + +| 属性 | 効果 | 例 | +|------|------|----| +| `#[input]` | 入力ポート | `#[input] posedge clk;` | +| `#[output]` | 出力ポート | `#[output] utiny led = 0xFF;` | +| `#[inout]` | 双方向ポート | `#[inout] ushort bus;` | + +### パラメータ属性 + +| 属性 | 効果 | 例 | +|------|------|----| +| `#[sv::param]` | `parameter` 宣言 | `#[sv::param] uint WIDTH = 8;` | + +### メモリ属性 + +| 属性 | 効果 | 例 | +|------|------|----| +| `#[sv::bram]` | `(* ram_style = "block" *)` | `#[sv::bram] utiny mem[1024];` | +| `#[sv::lutram]` | `(* ram_style = "distributed" *)` | `#[sv::lutram] utiny lut[16];` | + +### 合成ヒント + +| 属性 | 効果 | +|------|------| +| `#[sv::pipeline]` | パイプラインヒントコメント生成 | +| `#[sv::share]` | リソース共有ヒントコメント生成 | + +### クロック/タイミング + +| 属性 | 効果 | 例 | +|------|------|----| +| `#[sv::clock_domain("name")]` | `async func` のクロックを指定 | `#[sv::clock_domain("fast")]` | + +### 物理配置 (XDC/CST 生成) + +| 属性 | 効果 | 例 | +|------|------|----| +| `#[sv::pin("A1")]` | ピン割り当て | `#[sv::pin("H11")] #[input] posedge clk;` | +| `#[sv::iostandard("LVCMOS33")]` | IO 電圧規格 | `#[sv::iostandard("LVCMOS18")]` | + +--- + +## 14. 暗黙的変換 + +Cm の SV バックエンドは、開発者が意識せずとも正しい SV コードを生成するために +多数の暗黙的変換を行います。 + +### 14.1 代入方式の自動決定 + +| Cm コード | 文脈 | SV 出力 | +|----------|------|---------| +| `x = expr;` | `always_ff` 内 | `x <= expr;` (ノンブロッキング) | +| `x = expr;` | `always_comb` 内 | `x = expr;` (ブロッキング) | + +### 14.2 論理否定の変換 + +| Cm コード | SV 出力 | 理由 | +|----------|---------|------| +| `!flag` | `~flag` | 多ビット信号に安全な `~` に統一 | +| `~data` | `~data` | そのまま | + +### 14.3 リテラルのビット幅付与 + +| Cm コード | 代入先の型 | SV 出力 | +|----------|-----------|---------| +| `counter = 0;` | `uint` (32-bit) | `counter <= 32'd0;` | +| `flag = true;` | `bool` (1-bit) | `flag <= 1'b1;` | +| `val = 42;` | `utiny` (8-bit) | `val <= 8'd42;` | + +### 14.4 クロック/リセットの自動追加 + +| 条件 | 動作 | +|------|------| +| `async func` 存在 & `clk` 未宣言 | `input logic clk` を自動追加 | +| `async func` 存在 & `rst` 未宣言 | `input logic rst` を `clk` の後に自動追加 | + +### 14.5 MIR 一時変数のインライン展開 + +MIR で生成される `_tXXXX` 一時変数は、SV 出力時に元の式にインライン展開されます: + +``` +// MIR: _t1000 = counter + 1; result = _t1000; +// SV: result <= counter + 32'd1; (一時変数が消える) +``` + +### 14.6 `self.` プレフィックスの除去 + +``` +// MIR: self.counter → SV: counter +``` + +### 14.7 `else if` の正規化 + +ネストした `else { if ... }` パターンは SV の `else if` に正規化されます: + +```systemverilog +// ネストせず、フラットな else if チェーンを生成 +if (cond1) begin + ... +end else if (cond2) begin + ... +end else begin + ... +end +``` + +### 14.8 冗長な三項演算子の除去 + +`cond ? x : x` のような冗長な三項演算子は単純な代入 `x` に最適化されます。 + +--- + +## 15. コンパイルと検証 + +### コンパイル + +```bash +# SV コード生成 +cm compile --target=sv blink.cm -o blink.sv + +# テストベンチも同時生成 +cm compile --target=sv blink.cm -o blink.sv --testbench +``` + +### Verilator でのシミュレーション + +```bash +# Verilator でコンパイル + シミュレーション +verilator --sv --lint-only blink.sv # 構文チェック +verilator --sv --cc blink.sv --exe # シミュレーション +``` + +### Icarus Verilog での検証 + +```bash +iverilog -g2012 -o blink_sim blink.sv blink_tb.sv +vvp blink_sim +``` + +### FPGA ビルド (Gowin EDA) + +```bash +# Cm → SV → Gowin EDA → ビットストリーム +cm compile --target=sv blink.cm -o blink.sv +gw_sh gowin_build.tcl +``` + +--- + +## 16. 全体例: カウンタ付き LED 点滅 + +```cm +//! platform: sv + +// === ポート定義 === +#[input] posedge clk; +#[input] negedge rst_n; +#[output] bool led = false; + +// === 定数 === +const uint CLK_FREQ = 27_000_000; // 27MHz (Tang Console) +const uint CNT_MAX = CLK_FREQ / 2 - 1; + +// === 内部レジスタ === +uint counter = 0; + +// === 順序回路: 非同期リセット付き === +always void blink(posedge clk, negedge rst_n) { + if (rst_n == false) { + counter = 0; + led = false; + } else { + if (counter == CNT_MAX) { + counter = 0; + led = !led; + } else { + counter = counter + 1; + } + } +} +``` + +生成される SV: +```systemverilog +`timescale 1ns / 1ps + +module blink ( + input logic clk, + input logic rst_n, + output logic led +); + localparam CLK_FREQ = 32'd27000000; + localparam CNT_MAX = CLK_FREQ / 2 - 32'd1; + + logic [31:0] counter; + + always_ff @(posedge clk or negedge rst_n) begin + if (rst_n == 1'b0) begin + counter <= 32'd0; + led <= 1'b0; + end else begin + if (counter == CNT_MAX) begin + counter <= 32'd0; + led <= ~led; + end else begin + counter <= counter + 32'd1; + end + end + end +endmodule +``` + +--- + +## 17. 付録: トークン・キーワード一覧 + +### SV 固有トークン + +| トークン | キーワード | TypeKind | 用途 | +|---------|---------|----------|------| +| `KwPosedge` | `posedge` | `Posedge` | 立ち上がりエッジ | +| `KwNegedge` | `negedge` | `Negedge` | 立ち下がりエッジ | +| `KwWire` | `wire` | `Wire` | ワイヤ修飾型 | +| `KwReg` | `reg` | `Reg` | レジスタ修飾型 | +| `KwAlways` | `always` | — | ロジックブロック修飾子 | +| `KwAssign` | `assign` | — | 連続代入文 | +| `KwInitial` | `initial` | — | シミュレーション初期化 | +| `KwBit` | `bit` | `Bit` | 任意ビット幅型 | + +### 既存トークンの SV での意味 + +| トークン | 通常 (LLVM) の意味 | SV での意味 | +|---------|-------------------|------------| +| `async` | JS 非同期関数 | `always_ff` ブロック生成 (後方互換) | +| `func` | 関数宣言 | `always_comb` ブロック生成 | +| `void` | 戻り値なし関数 | ブロック生成 (ff/comb) | +| `=` | 変数代入 | ff 内: `<=`, comb 内: `=` | +| `!` | 論理否定 | `~` (ビット反転に統合) | +| `const` | 定数宣言 | `localparam` | +| `switch/case` | パターンマッチ | `case/endcase` | +| `enum` | 列挙型 | `typedef enum logic` | + +### SV 予約語 (モジュール名回避) + +以下の名前はモジュール名として使用できません: + +``` +output, input, inout, module, wire, reg, logic, begin, end, +if, else, for, while, case, default, assign, always, initial, +posedge, negedge, task, function, parameter, integer, real, time, event +``` From 6b2810a4e371b3d2d4c9dfb2d711be5dc154fe1b Mon Sep 17 00:00:00 2001 From: shadowlink0122 Date: Wed, 11 Mar 2026 21:19:59 +0900 Subject: [PATCH 08/12] =?UTF-8?q?=E3=83=89=E3=82=AD=E3=83=A5=E3=83=A1?= =?UTF-8?q?=E3=83=B3=E3=83=88:=20SV=E3=83=81=E3=83=A5=E3=83=BC=E3=83=88?= =?UTF-8?q?=E3=83=AA=E3=82=A2=E3=83=AB=E3=82=92docs/tutorials/=E3=81=AB?= =?UTF-8?q?=E6=97=A5=E8=8B=B1=E4=B8=A1=E8=A8=80=E8=AA=9E=E3=81=A7=E9=85=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - docs/tutorials/en/compiler/sv.md: 英語版(v0.15.1対応詳細版に更新) - docs/tutorials/ja/compiler/sv.md: 日本語版(v0.15.1対応詳細版に更新) - docs/tutorials/en/index.md: SVバックエンドリンクを追加 - docs/tutorials/ja/index.md: SVバックエンドリンクを追加、進捗トラッカー更新 - docs/v0.15.1/sv_tutorial.md: 削除(正しい場所に移動) 内容: 暗黙的変換8項目、SV属性11項目、トークン一覧、 ロジックブロック4パターン、完全な例(Cm→SV出力)を網羅 --- docs/tutorials/en/compiler/sv.md | 585 ++++++++++++++++++----- docs/tutorials/en/index.md | 1 + docs/tutorials/ja/compiler/sv.md | 583 ++++++++++++++++++----- docs/tutorials/ja/index.md | 5 +- docs/v0.15.1/sv_tutorial.md | 778 ------------------------------- 5 files changed, 920 insertions(+), 1032 deletions(-) delete mode 100644 docs/v0.15.1/sv_tutorial.md diff --git a/docs/tutorials/en/compiler/sv.md b/docs/tutorials/en/compiler/sv.md index 6b36a64d..e9167c9c 100644 --- a/docs/tutorials/en/compiler/sv.md +++ b/docs/tutorials/en/compiler/sv.md @@ -9,211 +9,542 @@ nav_order: 11 # Compiler - SystemVerilog Backend **Difficulty:** 🟡 Intermediate -**Time:** 30 min +**Time:** 45 min -Cm can generate SystemVerilog (SV) code to run as hardware on FPGAs. Compatible with Tang Console (Gowin), Xilinx, Intel, and more. +Cm can generate synthesizable SystemVerilog (SV) code for FPGAs. Compatible with Tang Console (Gowin), Xilinx, Intel, and more. -## Basic Usage +--- -```bash -# Generate SV -cm compile --target=sv program.cm -o output.sv +## Table of Contents + +1. [Your First Circuit](#your-first-circuit) +2. [Platform Directive](#platform-directive) +3. [Type System](#type-system) +4. [Port Declarations](#port-declarations) +5. [Logic Blocks](#logic-blocks) +6. [Operators](#operators) +7. [Literals and Bit Widths](#literals-and-bit-widths) +8. [Constants and localparam](#constants-and-localparam) +9. [Control Flow](#control-flow) +10. [Concatenation and Replication](#concatenation-and-replication) +11. [Enums (FSM)](#enums-fsm) +12. [SV Attributes](#sv-attributes) +13. [Implicit Conversions](#implicit-conversions) +14. [Compilation and Verification](#compilation-and-verification) +15. [Complete Example](#complete-example) +16. [Token Reference](#token-reference) -# A testbench is also auto-generated -# output_tb.sv is created alongside -``` - -## Port Declaration +--- -Use `#[input]` / `#[output]` attributes to declare I/O ports. +## Your First Circuit ```cm //! platform: sv -#[input] int a = 0; -#[input] int b = 0; -#[output] int sum = 0; +#[input] posedge clk; +#[input] bool rst = false; +#[output] bool led = false; -void adder() { - sum = a + b; +uint counter = 0; + +void blink(posedge clk) { + if (rst) { + counter = 0; + led = false; + } else { + if (counter == 49999999) { + counter = 0; + led = !led; + } else { + counter = counter + 1; + } + } } ``` -Generated SV: +Compile: +```bash +cm compile --target=sv blink.cm -o blink.sv +``` +Generated SV: ```systemverilog -module adder ( - input logic signed [31:0] a, - input logic signed [31:0] b, - output logic signed [31:0] sum +`timescale 1ns / 1ps + +module blink ( + input logic clk, + input logic rst, + output logic led ); + logic [31:0] counter; - // adder - always_comb begin - sum = a + b; + always_ff @(posedge clk) begin + if (rst) begin + counter <= 32'd0; + led <= 1'b0; + end else begin + if (counter == 32'd49999999) begin + counter <= 32'd0; + led <= ~led; + end else begin + counter <= counter + 32'd1; + end + end end - endmodule ``` -## Combinational and Sequential Logic +> **Key Points:** Cm's `=` is automatically converted to SV's `<=` (non-blocking assignment), +> and `!led` is converted to `~led` (bitwise inversion). -### Combinational Logic (always_comb) +--- -Regular functions are generated as combinational logic (`always_comb`). +## Platform Directive + +Every Cm file targeting SV **must** start with: ```cm //! platform: sv +``` -#[input] int a = 0; -#[input] int b = 0; -#[input] int c = 0; -#[output] int out = 0; +This enables: +- SV-specific keywords (`posedge`, `negedge`, `wire`, `reg`, `always`, `assign`) +- Non-synthesizable type validation (`float`, `string`, pointers → compile error) +- Implicit SV transformations (assignment style, literal bit widths, etc.) -void max3() { - if (a > b) { - if (a > c) { out = a; } else { out = c; } - } else { - if (b > c) { out = b; } else { out = c; } - } -} +--- + +## Type System + +### Basic Types + +| Cm Type | SV Output | Bits | Usage | +|---------|-----------|------|-------| +| `bool` | `logic` | 1 | Flags, control signals | +| `utiny` | `logic [7:0]` | 8 | Small counters, state | +| `ushort` | `logic [15:0]` | 16 | Addresses | +| `uint` | `logic [31:0]` | 32 | Counters, data | +| `ulong` | `logic [63:0]` | 64 | Timestamps | +| `tiny` | `logic signed [7:0]` | 8 | Signed small values | +| `short` | `logic signed [15:0]` | 16 | Signed medium values | +| `int` | `logic signed [31:0]` | 32 | Signed data | +| `long` | `logic signed [63:0]` | 64 | Signed large values | + +### SV-Specific Types + +| Cm Type | Purpose | SV Output | +|---------|---------|-----------| +| `posedge` | Rising edge signal | `logic` (1-bit) | +| `negedge` | Falling edge signal | `logic` (1-bit) | +| `wire` | Wire qualifier | `T` mapping | +| `reg` | Register qualifier | `T` mapping | + +### Custom Bit Widths + +```cm +#[output] bit[4] nibble; // → output logic [3:0] nibble +#[output] bit[12] address; // → output logic [11:0] address +bit[26] counter; // → logic [25:0] counter ``` +### Non-Synthesizable Types (Compile Error) + +`float`, `double`, `string`, `cstring`, `*T` (pointers), `&T` (references) are **rejected** by the SV backend. + +--- + +## Port Declarations + +```cm +// Input ports +#[input] posedge clk; // → input logic clk +#[input] bool rst = false; // → input logic rst +#[input] utiny data_in; // → input logic [7:0] data_in + +// Output ports +#[output] bool led = false; // → output logic led +#[output] utiny led_array = 0xFF; // → output logic [7:0] led_array + +// Bidirectional ports +#[inout] ushort bus; // → inout logic [15:0] bus + +// Parameters (overridable) +#[sv::param] uint WIDTH = 8; // → parameter WIDTH = 32'd8; +``` + +--- + +## Logic Blocks + ### Sequential Logic (always_ff) -Using `posedge` / `negedge` type parameters generates `always_ff` blocks. +#### Pattern A: `always` + Edge Parameter (Recommended) ```cm -//! platform: sv +always void counter_tick(posedge clk) { + count = count + 1; +} +// → always_ff @(posedge clk) begin +// count <= count + 32'd1; +// end +``` -#[output] uint counter = 0; -#[output] bool led = false; +#### Pattern B: Async Reset (Multiple Edges) -void blink(posedge clk, bool rst) { - if (rst) { - counter = 0; - led = false; +```cm +always void process(posedge clk, negedge rst_n) { + if (rst_n == false) { + count = 0; } else { - if (counter == 49999999) { - counter = 0; - led = !led; - } else { - counter = counter + 1; - } + count = count + 1; } } +// → always_ff @(posedge clk or negedge rst_n) begin ... ``` -Generated SV: +#### Pattern C: `void f(posedge clk)` (Legacy) + +```cm +void blink(posedge clk) { + led = !led; +} +// → always_ff @(posedge clk) begin led <= ~led; end +``` + +#### Pattern D: `async func` (Legacy) + +```cm +async func tick() { + counter = counter + 1; +} +// → always_ff @(posedge clk) begin counter <= counter + 32'd1; end +``` + +> **Note:** `async func` implicitly references the `clk` variable. +> If `clk` is undeclared, `input logic clk` is automatically added. + +### Combinational Logic (always_comb) + +Functions without edge parameters: + +```cm +always void decode() { + out = 0; + if (sel) { out = a; } + else { out = b; } +} +// → always_comb begin ... end +``` + +Legacy: `void f()` / `func f()` also map to `always_comb`. + +### Assignment Rules + +| Block Type | Cm Source | SV Output | +|-----------|----------|-----------| +| `always_ff` (sequential) | `x = expr;` | `x <= expr;` (non-blocking) | +| `always_comb` (combinational) | `x = expr;` | `x = expr;` (blocking) | + +Always write `=` in Cm — the compiler chooses the correct assignment style. + +--- + +## Operators + +### Arithmetic & Bitwise + +| Cm | SV | Notes | +|----|----|-------| +| `+` `-` `*` `/` `%` | Same | Arithmetic | +| `&` `\|` `^` `~` | Same | Bitwise | +| `<<` `>>` | Same | Shift | +| `==` `!=` `<` `<=` `>` `>=` | Same | Comparison | +| `&&` `\|\|` | Same | Logical | +| `!x` | `~x` | **Implicit conversion**: logical NOT → bitwise NOT | + +> **Important:** Cm's `!` (logical NOT) maps to SV's `~` (bitwise NOT) for multi-bit safety. + +--- + +## Literals and Bit Widths +Literals are **automatically given bit widths** based on context: + +| Cm Literal | Context Type | SV Output | +|-----------|-------------|-----------| +| `true` | `bool` | `1'b1` | +| `false` | `bool` | `1'b0` | +| `42` | `uint` (32-bit) | `32'd42` | +| `42` | `utiny` (8-bit) | `8'd42` | +| `-5` | `int` (signed 32-bit) | `-32'sd5` | + +### SV-Style Literals + +```cm +utiny mask = 8'b10101010; // → 8'b10101010 +ushort addr = 16'hFF00; // → 16'hFF00 +``` + +### Numeric Separators + +```cm +const uint CLK_FREQ = 50_000_000; // → localparam CLK_FREQ = 32'd50000000; +``` + +--- + +## Constants and localparam + +### `const` → `localparam` + +```cm +const uint CLK_FREQ = 27_000_000; +const uint CNT_MAX = CLK_FREQ / 2 - 1; +``` ```systemverilog -always_ff @(posedge clk) begin - if (rst) begin - counter <= 32'd0; - led <= 1'b0; - end else begin - if (counter == 32'd49999999) begin - counter <= 32'd0; - led <= !led; - end else begin - counter <= counter + 32'd1; - end - end -end +localparam CLK_FREQ = 32'd27000000; +localparam CNT_MAX = CLK_FREQ / 2 - 32'd1; ``` -## SV-Specific Types +### `#[sv::param]` → `parameter` + +```cm +#[sv::param] const uint WIDTH = 8; +// → parameter WIDTH = 32'd8; +``` -| Cm Type | Description | Generated SV | -|---------|-------------|-------------| -| `posedge` | Rising edge | `always_ff @(posedge ...)` | -| `negedge` | Falling edge | `always_ff @(negedge ...)` | -| `wire` | Wire | `wire` | -| `reg` | Register | `reg` | +--- -## SV Width-Qualified Literals +## Control Flow -SystemVerilog-style width-qualified literals can be written directly and are preserved in the SV output. +### if / else if / else ```cm -//! platform: sv +if (rst) { + counter = 0; +} else if (enable) { + counter = counter + 1; +} else { + // idle +} +``` +```systemverilog +if (rst) begin + counter <= 32'd0; +end else if (enable) begin + counter <= counter + 32'd1; +end else begin +end +``` -#[input] utiny sel = 0; -#[output] utiny out = 0; +### switch → case -void literal_test() { - if (sel == 0) { - out = 3'b101; // Binary: 3-bit width - } else if (sel == 1) { - out = 8'hFF; // Hexadecimal: 8-bit width - } else { - out = 8'd170; // Decimal: 8-bit width - } +```cm +switch (state) { + case 0: { next_state = 1; } + case 1: { next_state = 2; } + default: { next_state = 0; } } ``` +```systemverilog +case (state) + 32'd0: begin next_state <= 32'd1; end + 32'd1: begin next_state <= 32'd2; end + default: begin next_state <= 32'd0; end +endcase +``` -| Cm Source | SV Output | -|-----------|-----------| -| `3'b101` | `3'b101` | -| `8'hFF` | `8'hFF` | -| `8'd170` | `8'd170` | +--- -## Cm to SV Type Mapping +## Concatenation and Replication -| Cm Type | SV Bit Width | SV Type | -|---------|-------------|---------| -| `bool` | 1 | `logic` | -| `utiny` | 8 | `logic [7:0]` | -| `tiny` | 8 | `logic signed [7:0]` | -| `ushort` | 16 | `logic [15:0]` | -| `short` | 16 | `logic signed [15:0]` | -| `uint` | 32 | `logic [31:0]` | -| `int` | 32 | `logic signed [31:0]` | +```cm +result = {a, b}; // → {a, b} +replicated = {3{a}}; // → {3{a}} +``` -## BRAM Inference +Built-in functions (when `{...}` is ambiguous with blocks): -Arrays are inferred as Block RAM (BRAM). +```cm +result = concat(a, b); // → {a, b} +wide = replicate(nibble, 3); // → {3{nibble}} +``` + +--- + +## Enums (FSM) + +Cm `enum` maps to SV `typedef enum logic`. Bit width is auto-calculated: ```cm -//! platform: sv +enum State { IDLE, RUN, DONE, ERROR } +``` +```systemverilog +typedef enum logic [1:0] { + IDLE = 2'd0, RUN = 2'd1, DONE = 2'd2, ERROR = 2'd3 +} State; +``` -int memory[256]; -#[input] utiny addr = 0; -#[input] int wdata = 0; -#[input] bool we = false; -#[output] int rdata = 0; +--- -void bram_access(posedge clk) { - if (we) { - memory[addr] = wdata; - } - rdata = memory[addr]; -} +## SV Attributes + +| Attribute | Effect | Example | +|-----------|--------|---------| +| `#[input]` | Input port | `#[input] posedge clk;` | +| `#[output]` | Output port | `#[output] utiny led = 0xFF;` | +| `#[inout]` | Bidirectional port | `#[inout] ushort bus;` | +| `#[sv::param]` | `parameter` declaration | `#[sv::param] uint WIDTH = 8;` | +| `#[sv::bram]` | `(* ram_style = "block" *)` | `#[sv::bram] utiny mem[1024];` | +| `#[sv::lutram]` | `(* ram_style = "distributed" *)` | `#[sv::lutram] utiny lut[16];` | +| `#[sv::clock_domain("name")]` | Clock for `async func` | `#[sv::clock_domain("fast")]` | +| `#[sv::pipeline]` | Pipeline hint | | +| `#[sv::share]` | Resource sharing hint | | +| `#[sv::pin("XX")]` | Pin assignment (XDC/CST) | `#[sv::pin("H11")]` | +| `#[sv::iostandard("YY")]` | IO standard | `#[sv::iostandard("LVCMOS33")]` | + +--- + +## Implicit Conversions + +The SV backend performs many automatic conversions so you can write natural Cm code: + +### Assignment Style + +| Context | Cm | SV | +|---------|----|----| +| `always_ff` | `x = expr;` | `x <= expr;` | +| `always_comb` | `x = expr;` | `x = expr;` | + +### Logical NOT → Bitwise NOT + +| Cm | SV | Reason | +|----|----|----| +| `!flag` | `~flag` | Unified to `~` for multi-bit safety | + +### Literal Bit Width Inference + +| Cm | Target Type | SV | +|----|------------|-----| +| `counter = 0;` | `uint` | `counter <= 32'd0;` | +| `flag = true;` | `bool` | `flag <= 1'b1;` | + +### Auto Port Addition + +| Condition | Action | +|-----------|--------| +| `async func` exists & `clk` undeclared | `input logic clk` auto-added | +| `async func` exists & `rst` undeclared | `input logic rst` auto-added | + +### MIR Temporary Inlining + +MIR temporaries (`_tXXXX`) are inlined back into expressions: + +``` +MIR: _t1000 = counter + 1; result = _t1000; +SV: result <= counter + 32'd1; ``` -## Auto-Generated Testbench +### `self.` Prefix Removal + +`self.counter` → `counter` (SV has no `self`) + +### `else if` Normalization + +Nested `else { if ... }` patterns are flattened to `else if`. -Running `cm compile --target=sv` also generates a `_tb.sv` testbench. Verify with iverilog: +### Redundant Ternary Pruning + +`cond ? x : x` is simplified to `x`. + +--- + +## Compilation and Verification ```bash -# Compile and generate testbench -cm compile --target=sv program.cm -o output.sv +# Generate SV +cm compile --target=sv blink.cm -o blink.sv + +# Lint-only check with Verilator +verilator --sv --lint-only blink.sv -# Run simulation -iverilog -g2012 -o sim output.sv output_tb.sv +# Simulate with Icarus Verilog +iverilog -g2012 -o sim blink.sv blink_tb.sv vvp sim + +# FPGA build (Gowin EDA) +gw_sh gowin_build.tcl ``` -## Target FPGAs +### Target FPGAs | Board | Chip | Tool | |-------|------|------| -| Tang Console | Gowin | Gowin EDA | +| Tang Console 138K | Gowin GW5AST | Gowin EDA | | Tang Nano 9K | Gowin GW1NR-9 | Gowin EDA | | Arty A7 | Xilinx Artix-7 | Vivado | | DE10-Lite | Intel MAX 10 | Quartus | -> **Note:** In Gowin EDA, enable SystemVerilog via Project → Configuration → Synthesis → Verilog Language. +--- + +## Complete Example + +```cm +//! platform: sv + +#[input] posedge clk; +#[input] negedge rst_n; +#[output] bool led = false; + +const uint CLK_FREQ = 27_000_000; +const uint CNT_MAX = CLK_FREQ / 2 - 1; + +uint counter = 0; + +always void blink(posedge clk, negedge rst_n) { + if (rst_n == false) { + counter = 0; + led = false; + } else { + if (counter == CNT_MAX) { + counter = 0; + led = !led; + } else { + counter = counter + 1; + } + } +} +``` + +--- + +## Token Reference + +### SV-Specific Tokens + +| Token | Keyword | Purpose | +|-------|---------|---------| +| `KwPosedge` | `posedge` | Rising edge | +| `KwNegedge` | `negedge` | Falling edge | +| `KwWire` | `wire` | Wire qualifier | +| `KwReg` | `reg` | Register qualifier | +| `KwAlways` | `always` | Logic block modifier | +| `KwAssign` | `assign` | Continuous assignment | +| `KwInitial` | `initial` | Simulation initialization | +| `KwBit` | `bit` | Custom bit-width type | + +### Existing Tokens with SV Meaning + +| Token | Normal (LLVM) | SV Meaning | +|-------|--------------|------------| +| `async` | JS async function | `always_ff` (legacy) | +| `func` | Function declaration | `always_comb` | +| `void` | No return value | Block generation | +| `=` | Variable assignment | ff: `<=`, comb: `=` | +| `!` | Logical NOT | `~` (bitwise NOT) | +| `const` | Constant | `localparam` | +| `switch/case` | Pattern match | `case/endcase` | +| `enum` | Enumeration | `typedef enum logic` | --- @@ -222,4 +553,4 @@ vvp sim --- -**Last updated:** 2026-03-09 +**Last updated:** 2026-03-11 diff --git a/docs/tutorials/en/index.md b/docs/tutorials/en/index.md index 71edbffa..7cf0dfce 100644 --- a/docs/tutorials/en/index.md +++ b/docs/tutorials/en/index.md @@ -75,6 +75,7 @@ Estimated Time: 3 hours - [LLVM Backend](compiler/llvm.html) - Native compilation - [WASM Backend](compiler/wasm.html) - WebAssembly output - [JS Backend](compiler/js-compilation.html) - JavaScript output + - [SV Backend](compiler/sv.html) - SystemVerilog / FPGA output 🆕 - [UEFI Baremetal](compiler/uefi.html) - UEFI application development (no_std) - [Preprocessor](compiler/preprocessor.html) - Conditional compilation - [Linter](compiler/linter.html) - Static analysis (cm lint) diff --git a/docs/tutorials/ja/compiler/sv.md b/docs/tutorials/ja/compiler/sv.md index 12ee5ced..3216da57 100644 --- a/docs/tutorials/ja/compiler/sv.md +++ b/docs/tutorials/ja/compiler/sv.md @@ -9,211 +9,542 @@ nav_order: 11 # コンパイラ編 - SystemVerilogバックエンド **難易度:** 🟡 中級 -**所要時間:** 30分 +**所要時間:** 45分 CmからSystemVerilog (SV) を生成し、FPGA上でハードウェアとして動作させることができます。Tang Console(Gowin)、Xilinx、Intel等のFPGAに対応しています。 -## 基本的な使い方 - -```bash -# SV生成 -cm compile --target=sv program.cm -o output.sv +--- -# テストベンチも自動生成される -# output_tb.sv が同時に生成 -``` +## 目次 + +1. [最初の回路](#最初の回路) +2. [プラットフォームディレクティブ](#プラットフォームディレクティブ) +3. [型システム](#型システム) +4. [ポート宣言](#ポート宣言) +5. [ロジックブロック](#ロジックブロック) +6. [演算子](#演算子) +7. [定数リテラルとビット幅](#定数リテラルとビット幅) +8. [定数とlocalparam](#定数とlocalparam) +9. [制御構文](#制御構文) +10. [連接と複製](#連接と複製) +11. [列挙型 (FSM)](#列挙型-fsm) +12. [SV属性](#sv属性) +13. [暗黙的変換](#暗黙的変換) +14. [コンパイルと検証](#コンパイルと検証) +15. [全体例](#全体例) +16. [トークンリファレンス](#トークンリファレンス) -## ポート宣言 +--- -`#[input]` / `#[output]` アトリビュートで入出力ポートを宣言します。 +## 最初の回路 ```cm //! platform: sv -#[input] int a = 0; -#[input] int b = 0; -#[output] int sum = 0; +#[input] posedge clk; +#[input] bool rst = false; +#[output] bool led = false; -void adder() { - sum = a + b; +uint counter = 0; + +void blink(posedge clk) { + if (rst) { + counter = 0; + led = false; + } else { + if (counter == 49999999) { + counter = 0; + led = !led; + } else { + counter = counter + 1; + } + } } ``` -生成されるSV: +コンパイル: +```bash +cm compile --target=sv blink.cm -o blink.sv +``` +生成されるSV: ```systemverilog -module adder ( - input logic signed [31:0] a, - input logic signed [31:0] b, - output logic signed [31:0] sum +`timescale 1ns / 1ps + +module blink ( + input logic clk, + input logic rst, + output logic led ); + logic [31:0] counter; - // adder - always_comb begin - sum = a + b; + always_ff @(posedge clk) begin + if (rst) begin + counter <= 32'd0; + led <= 1'b0; + end else begin + if (counter == 32'd49999999) begin + counter <= 32'd0; + led <= ~led; + end else begin + counter <= counter + 32'd1; + end + end end - endmodule ``` -## 組み合わせ回路と順序回路 +> **ポイント:** Cmの `=` は自動的にSVの `<=` (ノンブロッキング代入) に変換されます。 +> `!led` もSVの `~led` (ビット反転) に変換されます。 + +--- -### 組み合わせ回路(always_comb) +## プラットフォームディレクティブ -通常の関数は組み合わせ回路(`always_comb`)として生成されます。 +SVバックエンドを使用するには、ファイル先頭に **必ず** 記述します: ```cm //! platform: sv +``` + +有効になる機能: +- SV固有キーワード (`posedge`, `negedge`, `wire`, `reg`, `always`, `assign`) +- 非合成型のバリデーション (`float`, `string`, ポインタ → コンパイルエラー) +- 暗黙的SV変換 (代入方式、リテラルビット幅付与 等) + +--- + +## 型システム + +### 基本型 + +| Cm型 | SV出力 | ビット幅 | 用途 | +|------|--------|---------|------| +| `bool` | `logic` | 1 | フラグ、制御信号 | +| `utiny` | `logic [7:0]` | 8 | 小さなカウンタ、状態 | +| `ushort` | `logic [15:0]` | 16 | アドレス | +| `uint` | `logic [31:0]` | 32 | カウンタ、データ | +| `ulong` | `logic [63:0]` | 64 | タイムスタンプ | +| `tiny` | `logic signed [7:0]` | 8 | 符号付き小数値 | +| `short` | `logic signed [15:0]` | 16 | 符号付き中間値 | +| `int` | `logic signed [31:0]` | 32 | 符号付きデータ | +| `long` | `logic signed [63:0]` | 64 | 符号付き大規模データ | + +### SV固有型 + +| Cm型 | 用途 | SV出力 | +|------|------|--------| +| `posedge` | クロック立ち上がりエッジ信号 | `logic` (1-bit) | +| `negedge` | クロック/リセット立ち下がりエッジ信号 | `logic` (1-bit) | +| `wire` | ワイヤ修飾(組み合わせ出力) | `T`のマッピングに準拠 | +| `reg` | レジスタ修飾(順序回路出力) | `T`のマッピングに準拠 | + +### カスタムビット幅 + +```cm +#[output] bit[4] nibble; // → output logic [3:0] nibble +#[output] bit[12] address; // → output logic [11:0] address +bit[26] counter; // → logic [25:0] counter +``` + +### 非合成型 (コンパイルエラー) + +`float`, `double`, `string`, `cstring`, `*T` (ポインタ), `&T` (参照) はSVバックエンドで **コンパイルエラー** になります。 + +--- + +## ポート宣言 + +```cm +// 入力ポート +#[input] posedge clk; // → input logic clk +#[input] bool rst = false; // → input logic rst +#[input] utiny data_in; // → input logic [7:0] data_in -#[input] int a = 0; -#[input] int b = 0; -#[input] int c = 0; -#[output] int out = 0; +// 出力ポート +#[output] bool led = false; // → output logic led +#[output] utiny led_array = 0xFF; // → output logic [7:0] led_array -void max3() { - if (a > b) { - if (a > c) { out = a; } else { out = c; } +// 双方向ポート +#[inout] ushort bus; // → inout logic [15:0] bus + +// パラメータ(外部から上書き可能) +#[sv::param] uint WIDTH = 8; // → parameter WIDTH = 32'd8; +``` + +--- + +## ロジックブロック + +### 順序回路 (always_ff) + +#### パターンA: `always` + エッジパラメータ (推奨) + +```cm +always void counter_tick(posedge clk) { + count = count + 1; +} +// → always_ff @(posedge clk) begin +// count <= count + 32'd1; +// end +``` + +#### パターンB: 非同期リセット(複数エッジ) + +```cm +always void process(posedge clk, negedge rst_n) { + if (rst_n == false) { + count = 0; } else { - if (b > c) { out = b; } else { out = c; } + count = count + 1; } } +// → always_ff @(posedge clk or negedge rst_n) begin ... ``` -### 順序回路(always_ff) +#### パターンC: `void f(posedge clk)` (後方互換) + +```cm +void blink(posedge clk) { + led = !led; +} +// → always_ff @(posedge clk) begin led <= ~led; end +``` -`posedge` / `negedge` 型パラメータを使うと `always_ff` ブロックが生成されます。 +#### パターンD: `async func` (後方互換) ```cm -//! platform: sv +async func tick() { + counter = counter + 1; +} +// → always_ff @(posedge clk) begin counter <= counter + 32'd1; end +``` -#[output] uint counter = 0; -#[output] bool led = false; +> **注意:** `async func` は暗黙的に `clk` 変数を参照します。 +> `clk` が未宣言の場合、自動的に `input logic clk` が追加されます。 -void blink(posedge clk, bool rst) { - if (rst) { - counter = 0; - led = false; - } else { - if (counter == 49999999) { - counter = 0; - led = !led; - } else { - counter = counter + 1; - } - } +### 組み合わせ回路 (always_comb) + +エッジパラメータなしの関数: + +```cm +always void decode() { + out = 0; + if (sel) { out = a; } + else { out = b; } } +// → always_comb begin ... end ``` -生成されるSV: +後方互換: `void f()` / `func f()` も `always_comb` に変換されます。 + +### 代入の自動変換ルール + +| ブロック種別 | Cmでの記述 | SV出力 | +|------------|----------|--------| +| `always_ff` (順序回路) | `x = expr;` | `x <= expr;` (ノンブロッキング) | +| `always_comb` (組み合わせ) | `x = expr;` | `x = expr;` (ブロッキング) | + +Cmでは常に `=` で記述し、コンパイラが文脈に応じて適切な代入方式を選択します。 + +--- + +## 演算子 + +### 算術・ビット演算 + +| Cm | SV | 備考 | +|----|----|------| +| `+` `-` `*` `/` `%` | 同じ | 算術 | +| `&` `\|` `^` `~` | 同じ | ビット演算 | +| `<<` `>>` | 同じ | シフト | +| `==` `!=` `<` `<=` `>` `>=` | 同じ | 比較 | +| `&&` `\|\|` | 同じ | 論理演算 | +| `!x` | `~x` | **暗黙変換**: 論理否定→ビット反転に統合 | + +> **重要:** Cmの `!` (論理否定) はSVでは `~` (ビット反転) にマッピングされます。多ビット信号に対して安全な `~` に統一しています。 + +--- + +## 定数リテラルとビット幅 + +リテラルは文脈の型に基づき **自動的にビット幅付き** に変換されます: +| Cmリテラル | 文脈の型 | SV出力 | +|-----------|---------|--------| +| `true` | `bool` | `1'b1` | +| `false` | `bool` | `1'b0` | +| `42` | `uint` (32-bit) | `32'd42` | +| `42` | `utiny` (8-bit) | `8'd42` | +| `-5` | `int` (符号付き32-bit) | `-32'sd5` | + +### SVスタイルリテラル + +```cm +utiny mask = 8'b10101010; // → 8'b10101010 +ushort addr = 16'hFF00; // → 16'hFF00 +``` + +### 数値区切り文字 + +```cm +const uint CLK_FREQ = 50_000_000; // → localparam CLK_FREQ = 32'd50000000; +``` + +--- + +## 定数とlocalparam + +### `const` → `localparam` + +```cm +const uint CLK_FREQ = 27_000_000; +const uint CNT_MAX = CLK_FREQ / 2 - 1; +``` ```systemverilog -always_ff @(posedge clk) begin - if (rst) begin - counter <= 32'd0; - led <= 1'b0; - end else begin - if (counter == 32'd49999999) begin - counter <= 32'd0; - led <= !led; - end else begin - counter <= counter + 32'd1; - end - end -end +localparam CLK_FREQ = 32'd27000000; +localparam CNT_MAX = CLK_FREQ / 2 - 32'd1; ``` -## SV固有型 +### `#[sv::param]` → `parameter` + +```cm +#[sv::param] const uint WIDTH = 8; +// → parameter WIDTH = 32'd8; +``` -| Cm型 | 説明 | 生成されるSV | -|------|------|------------| -| `posedge` | 立ち上がりエッジ | `always_ff @(posedge ...)` | -| `negedge` | 立ち下がりエッジ | `always_ff @(negedge ...)` | -| `wire` | ワイヤ | `wire` | -| `reg` | レジスタ | `reg` | +--- -## SV幅付きリテラル +## 制御構文 -SystemVerilog形式の幅付きリテラルを直接記述でき、SV出力でもそのまま保持されます。 +### if / else if / else ```cm -//! platform: sv +if (rst) { + counter = 0; +} else if (enable) { + counter = counter + 1; +} else { + // idle +} +``` +```systemverilog +if (rst) begin + counter <= 32'd0; +end else if (enable) begin + counter <= counter + 32'd1; +end else begin +end +``` -#[input] utiny sel = 0; -#[output] utiny out = 0; +### switch → case -void literal_test() { - if (sel == 0) { - out = 3'b101; // 2進数: 3ビット幅 - } else if (sel == 1) { - out = 8'hFF; // 16進数: 8ビット幅 - } else { - out = 8'd170; // 10進数: 8ビット幅 - } +```cm +switch (state) { + case 0: { next_state = 1; } + case 1: { next_state = 2; } + default: { next_state = 0; } } ``` +```systemverilog +case (state) + 32'd0: begin next_state <= 32'd1; end + 32'd1: begin next_state <= 32'd2; end + default: begin next_state <= 32'd0; end +endcase +``` -| Cm記述 | SV出力 | -|--------|--------| -| `3'b101` | `3'b101` | -| `8'hFF` | `8'hFF` | -| `8'd170` | `8'd170` | +--- -## Cm型とSV型の対応 +## 連接と複製 -| Cm型 | SVビット幅 | SV型 | -|------|-----------|------| -| `bool` | 1 | `logic` | -| `utiny` | 8 | `logic [7:0]` | -| `tiny` | 8 | `logic signed [7:0]` | -| `ushort` | 16 | `logic [15:0]` | -| `short` | 16 | `logic signed [15:0]` | -| `uint` | 32 | `logic [31:0]` | -| `int` | 32 | `logic signed [31:0]` | +```cm +result = {a, b}; // → {a, b} +replicated = {3{a}}; // → {3{a}} +``` -## BRAM推論 +ビルトイン関数(`{...}` がブロックと曖昧な場合): -配列はBlock RAM(BRAM)として推論されます。 +```cm +result = concat(a, b); // → {a, b} +wide = replicate(nibble, 3); // → {3{nibble}} +``` + +--- + +## 列挙型 (FSM) + +Cmの `enum` はSVの `typedef enum logic` に変換されます。ビット幅はバリアント数から自動計算: ```cm -//! platform: sv +enum State { IDLE, RUN, DONE, ERROR } +``` +```systemverilog +typedef enum logic [1:0] { + IDLE = 2'd0, RUN = 2'd1, DONE = 2'd2, ERROR = 2'd3 +} State; +``` -int memory[256]; -#[input] utiny addr = 0; -#[input] int wdata = 0; -#[input] bool we = false; -#[output] int rdata = 0; +--- + +## SV属性 + +| 属性 | 効果 | 例 | +|------|------|----| +| `#[input]` | 入力ポート | `#[input] posedge clk;` | +| `#[output]` | 出力ポート | `#[output] utiny led = 0xFF;` | +| `#[inout]` | 双方向ポート | `#[inout] ushort bus;` | +| `#[sv::param]` | `parameter`宣言 | `#[sv::param] uint WIDTH = 8;` | +| `#[sv::bram]` | `(* ram_style = "block" *)` | `#[sv::bram] utiny mem[1024];` | +| `#[sv::lutram]` | `(* ram_style = "distributed" *)` | `#[sv::lutram] utiny lut[16];` | +| `#[sv::clock_domain("name")]` | `async func`のクロック指定 | `#[sv::clock_domain("fast")]` | +| `#[sv::pipeline]` | パイプラインヒント | | +| `#[sv::share]` | リソース共有ヒント | | +| `#[sv::pin("XX")]` | ピン割り当て (XDC/CST) | `#[sv::pin("H11")]` | +| `#[sv::iostandard("YY")]` | IO電圧規格 | `#[sv::iostandard("LVCMOS33")]` | + +--- + +## 暗黙的変換 + +SVバックエンドは、正しいSVコードを自動生成するために多数の暗黙的変換を行います。 + +### 代入方式の自動決定 + +| 文脈 | Cm | SV | +|------|----|----| +| `always_ff` | `x = expr;` | `x <= expr;` | +| `always_comb` | `x = expr;` | `x = expr;` | + +### 論理否定の変換 + +| Cm | SV | 理由 | +|----|----|----| +| `!flag` | `~flag` | 多ビット信号に安全な `~` に統一 | + +### リテラルのビット幅付与 + +| Cm | 代入先の型 | SV | +|----|-----------|-----| +| `counter = 0;` | `uint` | `counter <= 32'd0;` | +| `flag = true;` | `bool` | `flag <= 1'b1;` | + +### クロック/リセットの自動追加 + +| 条件 | 動作 | +|------|------| +| `async func` 存在 & `clk` 未宣言 | `input logic clk` を自動追加 | +| `async func` 存在 & `rst` 未宣言 | `input logic rst` を自動追加 | + +### MIR一時変数のインライン展開 + +MIRの `_tXXXX` 一時変数は元の式にインライン展開されます: -void bram_access(posedge clk) { - if (we) { - memory[addr] = wdata; - } - rdata = memory[addr]; -} ``` +MIR: _t1000 = counter + 1; result = _t1000; +SV: result <= counter + 32'd1; +``` + +### `self.` プレフィックスの除去 + +`self.counter` → `counter` (SVに `self` は不要) -## テストベンチ自動生成 +### `else if` の正規化 -`cm compile --target=sv` を実行すると `_tb.sv` テストベンチも自動生成されます。iverilogで検証可能: +ネストした `else { if ... }` パターンを `else if` にフラット化。 + +### 冗長な三項演算子の除去 + +`cond ? x : x` を単純な `x` に最適化。 + +--- + +## コンパイルと検証 ```bash -# コンパイルとテストベンチ生成 -cm compile --target=sv program.cm -o output.sv +# SV コード生成 +cm compile --target=sv blink.cm -o blink.sv + +# Verilatorで構文チェック +verilator --sv --lint-only blink.sv -# シミュレーション実行 -iverilog -g2012 -o sim output.sv output_tb.sv +# Icarus Verilogでシミュレーション +iverilog -g2012 -o sim blink.sv blink_tb.sv vvp sim + +# FPGA ビルド (Gowin EDA) +gw_sh gowin_build.tcl ``` -## ターゲットFPGA +### ターゲットFPGA | ボード | チップ | ツール | |--------|--------|--------| -| Tang Console | Gowin | Gowin EDA | +| Tang Console 138K | Gowin GW5AST | Gowin EDA | | Tang Nano 9K | Gowin GW1NR-9 | Gowin EDA | | Arty A7 | Xilinx Artix-7 | Vivado | | DE10-Lite | Intel MAX 10 | Quartus | -> **Note:** Gowin EDAでは Project → Configuration → Synthesis → Verilog Language でSystemVerilogを有効にしてください。 +--- + +## 全体例 + +```cm +//! platform: sv + +#[input] posedge clk; +#[input] negedge rst_n; +#[output] bool led = false; + +const uint CLK_FREQ = 27_000_000; +const uint CNT_MAX = CLK_FREQ / 2 - 1; + +uint counter = 0; + +always void blink(posedge clk, negedge rst_n) { + if (rst_n == false) { + counter = 0; + led = false; + } else { + if (counter == CNT_MAX) { + counter = 0; + led = !led; + } else { + counter = counter + 1; + } + } +} +``` + +--- + +## トークンリファレンス + +### SV固有トークン + +| トークン | キーワード | 用途 | +|---------|---------|------| +| `KwPosedge` | `posedge` | 立ち上がりエッジ | +| `KwNegedge` | `negedge` | 立ち下がりエッジ | +| `KwWire` | `wire` | ワイヤ修飾型 | +| `KwReg` | `reg` | レジスタ修飾型 | +| `KwAlways` | `always` | ロジックブロック修飾子 | +| `KwAssign` | `assign` | 連続代入文 | +| `KwInitial` | `initial` | シミュレーション初期化 | +| `KwBit` | `bit` | 任意ビット幅型 | + +### 既存トークンのSVでの意味 + +| トークン | 通常(LLVM)の意味 | SVでの意味 | +|---------|-----------------|-----------| +| `async` | JS非同期関数 | `always_ff` (後方互換) | +| `func` | 関数宣言 | `always_comb` | +| `void` | 戻り値なし関数 | ブロック生成 | +| `=` | 変数代入 | ff: `<=`, comb: `=` | +| `!` | 論理否定 | `~` (ビット反転に統合) | +| `const` | 定数宣言 | `localparam` | +| `switch/case` | パターンマッチ | `case/endcase` | +| `enum` | 列挙型 | `typedef enum logic` | --- @@ -222,4 +553,4 @@ vvp sim --- -**最終更新:** 2026-03-09 +**最終更新:** 2026-03-11 diff --git a/docs/tutorials/ja/index.md b/docs/tutorials/ja/index.md index 8d7cd72b..828d7e41 100644 --- a/docs/tutorials/ja/index.md +++ b/docs/tutorials/ja/index.md @@ -94,6 +94,7 @@ Cm言語の全機能を段階的に学べる包括的なチュートリアル集 - [LLVMバックエンド](compiler/llvm.html) - ネイティブコンパイル - [WASMバックエンド](compiler/wasm.html) - WebAssembly出力 - [JSバックエンド](compiler/js-compilation.html) - JavaScript出力 + - [SVバックエンド](compiler/sv.html) - SystemVerilog / FPGA出力 🆕 - [UEFIベアメタル](compiler/uefi.html) - UEFIアプリケーション開発(no_std) - [プリプロセッサ](compiler/preprocessor.html) - 条件付きコンパイル - [Linter](compiler/linter.html) - 静的解析(cm lint) @@ -169,6 +170,7 @@ Cm言語の全機能を段階的に学べる包括的なチュートリアル集 | | Formatter | ✅ | - | - | ✅ [formatter](compiler/formatter.html) | | | プリプロセッサ | ✅ | ✅ | ❌ | ✅ [preprocessor](compiler/preprocessor.html) | | **バックエンド** | JSコンパイル | - | - | ✅ | ✅ [js-compilation](compiler/js-compilation.html) | +| | SVバックエンド | ✅ | ❌ | ❌ | ✅ [sv](compiler/sv.html) | | | UEFIベアメタル | ✅ | ❌ | ❌ | ✅ [uefi](compiler/uefi.html) | 凡例: ✅ 完全対応 | ⚠️ 部分対応 | ❌ 未対応 @@ -242,11 +244,12 @@ Cm言語の全機能を段階的に学べる包括的なチュートリアル集 - [ ] mustキーワード - [ ] マクロ -- [ ] コンパイラ編(9チュートリアル) +- [ ] コンパイラ編(10チュートリアル) - [ ] コンパイラの使い方 - [ ] LLVMバックエンド - [ ] WASMバックエンド - [ ] JSバックエンド + - [ ] SVバックエンド 🆕 - [ ] UEFIベアメタル - [ ] プリプロセッサ - [ ] Linter diff --git a/docs/v0.15.1/sv_tutorial.md b/docs/v0.15.1/sv_tutorial.md deleted file mode 100644 index 54e673f6..00000000 --- a/docs/v0.15.1/sv_tutorial.md +++ /dev/null @@ -1,778 +0,0 @@ -# Cm SystemVerilog チュートリアル - -Cm コンパイラの SV バックエンドを使用して、FPGA 向けの SystemVerilog コードを生成するための包括的なガイドです。 - ---- - -## 目次 - -1. [はじめに](#1-はじめに) -2. [最初の回路: LED 点滅](#2-最初の回路-led-点滅) -3. [プラットフォームディレクティブ](#3-プラットフォームディレクティブ) -4. [型システム](#4-型システム) -5. [ポート宣言](#5-ポート宣言) -6. [ロジックブロック](#6-ロジックブロック) -7. [演算子](#7-演算子) -8. [定数リテラルとビット幅](#8-定数リテラルとビット幅) -9. [定数と localparam](#9-定数と-localparam) -10. [制御構文](#10-制御構文) -11. [連接と複製](#11-連接と複製) -12. [列挙型 (FSM)](#12-列挙型-fsm) -13. [SV 属性](#13-sv-属性) -14. [暗黙的変換](#14-暗黙的変換) -15. [コンパイルと検証](#15-コンパイルと検証) -16. [全体例: カウンタ付き LED 点滅](#16-全体例-カウンタ付き-led-点滅) -17. [付録: トークン・キーワード一覧](#17-付録-トークンキーワード一覧) - ---- - -## 1. はじめに - -Cm の SV バックエンドは、Cm の既存構文を活用して **合成可能な SystemVerilog** を生成します。 -ソフトウェア開発者にとって馴染み深い Cm の構文で FPGA 回路を記述でき、 -コンパイラが適切な SV 構文(`always_ff`, `always_comb`, `<=` 代入等)に自動変換します。 - -### 設計哲学 - -- **Cm の構文を最大限活用**: 新しいキーワードは最小限にし、既存の `if/else`, `switch`, `enum` 等をそのまま使用 -- **暗黙的な SV マッピング**: `=` 代入は文脈に応じて `<=` (ノンブロッキング) と `=` (ブロッキング) に自動変換 -- **型安全なハードウェア記述**: 非合成型(`float`, `string`, ポインタ)はコンパイルエラー -- **1 ファイル = 1 モジュール**: ファイル名がモジュール名になる - ---- - -## 2. 最初の回路: LED 点滅 - -```cm -//! platform: sv - -#[input] posedge clk; -#[input] bool rst = false; -#[output] bool led = false; - -uint counter = 0; - -void blink(posedge clk) { - if (rst) { - counter = 0; - led = false; - } else { - if (counter == 49999999) { - counter = 0; - led = !led; - } else { - counter = counter + 1; - } - } -} -``` - -コンパイル: -```bash -cm compile --target=sv blink.cm -o blink.sv -``` - -生成される SV: -```systemverilog -`timescale 1ns / 1ps - -module blink ( - input logic clk, - input logic rst, - output logic led -); - logic [31:0] counter; - - always_ff @(posedge clk) begin - if (rst) begin - counter <= 32'd0; - led <= 1'b0; - end else begin - if (counter == 32'd49999999) begin - counter <= 32'd0; - led <= ~led; - end else begin - counter <= counter + 32'd1; - end - end - end -endmodule -``` - -> **ポイント**: Cm の `=` が自動的に SV の `<=` (ノンブロッキング代入) に変換されています。 -> `!led` も SV の `~led` に変換されています。 - ---- - -## 3. プラットフォームディレクティブ - -SV バックエンドを使用するには、ファイル先頭に **必ず** 以下のディレクティブを記述します: - -```cm -//! platform: sv -``` - -このディレクティブにより: -- `posedge`, `negedge`, `wire`, `reg` 等の SV 固有キーワードが有効化 -- 非合成型(`float`, `string`, ポインタ)に対するバリデーションが有効化 -- `always`, `assign`, `initial` 等の SV 構文が使用可能 - ---- - -## 4. 型システム - -### 4.1 基本型と SV マッピング - -| Cm 型 | SV 出力 | ビット幅 | 用途 | -|-------|---------|---------|------| -| `bool` | `logic` | 1 | フラグ、制御信号 | -| `utiny` | `logic [7:0]` | 8 | 小さなカウンタ、状態 | -| `ushort` | `logic [15:0]` | 16 | アドレス、中間値 | -| `uint` | `logic [31:0]` | 32 | カウンタ、データ | -| `ulong` | `logic [63:0]` | 64 | タイムスタンプ、大規模データ | -| `tiny` | `logic signed [7:0]` | 8 | 符号付き小数値 | -| `short` | `logic signed [15:0]` | 16 | 符号付き中間値 | -| `int` | `logic signed [31:0]` | 32 | 符号付きデータ | -| `long` | `logic signed [63:0]` | 64 | 符号付き大規模データ | - -### 4.2 SV 固有型 - -| Cm 型 | 用途 | SV 出力 | -|-------|------|---------| -| `posedge` | クロック立ち上がりエッジ信号 | `logic` (1-bit) | -| `negedge` | クロック/リセット立ち下がりエッジ信号 | `logic` (1-bit) | -| `wire` | ワイヤ修飾(組み合わせ出力) | `T` のマッピングに準拠 | -| `reg` | レジスタ修飾(順序回路出力) | `T` のマッピングに準拠 | - -### 4.3 カスタムビット幅 - -任意のビット幅を `bit[N]` で指定: - -```cm -#[output] bit[4] nibble; // → output logic [3:0] nibble -#[output] bit[12] address; // → output logic [11:0] address -bit[26] counter; // → logic [25:0] counter -``` - -### 4.4 非合成型 (コンパイルエラー) - -以下の型は SV バックエンドで **コンパイルエラー** になります: - -- `float`, `double` — 浮動小数点 -- `string`, `cstring` — 文字列 -- `*T` (ポインタ), `&T` (参照) - ---- - -## 5. ポート宣言 - -ポートは属性付きグローバル変数で宣言します: - -```cm -// 入力ポート -#[input] posedge clk; // → input logic clk -#[input] bool rst = false; // → input logic rst -#[input] utiny data_in; // → input logic [7:0] data_in - -// 出力ポート -#[output] bool led = false; // → output logic led -#[output] utiny led_array = 0xFF; // → output logic [7:0] led_array -#[output] uint data_out; // → output logic [31:0] data_out - -// 双方向ポート -#[inout] ushort bus; // → inout logic [15:0] bus - -// パラメータ(外部から上書き可能) -#[sv::param] uint WIDTH = 8; // → parameter WIDTH = 32'd8; -``` - -> **初期値**: ポートの初期値(`= false`, `= 0xFF` 等)はポート宣言には反映されず、 -> 内部ロジックのリセット値として使用されます。 - ---- - -## 6. ロジックブロック - -### 6.1 順序回路 (always_ff) - -#### パターン A: `always` + エッジパラメータ (推奨) - -```cm -always void counter(posedge clk) { - count = count + 1; -} -// → always_ff @(posedge clk) begin -// count <= count + 32'd1; -// end -``` - -#### パターン B: 非同期リセット付き(複数エッジ) - -```cm -always void process(posedge clk, negedge rst_n) { - if (rst_n == false) { - count = 0; - } else { - count = count + 1; - } -} -// → always_ff @(posedge clk or negedge rst_n) begin -// if (rst_n == 1'b0) begin -// count <= 32'd0; -// end else begin -// count <= count + 32'd1; -// end -// end -``` - -#### パターン C: `void f(posedge clk)` (後方互換) - -```cm -void blink(posedge clk) { - led = !led; -} -// → always_ff @(posedge clk) begin -// led <= ~led; -// end -``` - -#### パターン D: `async func` (後方互換) - -```cm -async func tick() { - counter = counter + 1; -} -// → always_ff @(posedge clk) begin -// counter <= counter + 32'd1; -// end -``` - -> **注意**: `async func` は暗黙的に `clk` 変数を参照します。 -> `clk` が未宣言の場合、自動的に `input logic clk` が追加されます。 - -### 6.2 組み合わせ回路 (always_comb) - -エッジパラメータなしの関数は `always_comb` に変換されます: - -```cm -always void decode() { - out = 0; // デフォルト値(ラッチ防止) - if (sel) { - out = a; - } else { - out = b; - } -} -// → always_comb begin -// out = 32'd0; -// if (sel) begin out = a; end -// else begin out = b; end -// end -``` - -トリガなし `void f()` / `func f()` も `always_comb` に変換されます(後方互換): - -```cm -void update() { - signal = (counter > 100); -} -// → always_comb begin -// signal = (counter > 32'd100); -// end -``` - -### 6.3 代入の自動変換ルール - -| ブロック種別 | Cm での記述 | SV 出力 | -|------------|-----------|---------| -| `always_ff` (順序回路) | `x = expr;` | `x <= expr;` (ノンブロッキング) | -| `always_comb` (組み合わせ) | `x = expr;` | `x = expr;` (ブロッキング) | - -Cm では常に `=` で記述し、コンパイラが文脈に応じて適切な代入方式を選択します。 - ---- - -## 7. 演算子 - -### 7.1 算術演算子 - -| Cm | SV | 例 | -|----|----|----| -| `+` | `+` | `counter + 1` → `counter + 32'd1` | -| `-` | `-` | `a - b` | -| `*` | `*` | `a * b` | -| `/` | `/` | `a / b` | -| `%` | `%` | `a % 10` | - -### 7.2 ビット演算子 - -| Cm | SV | 例 | -|----|----|----| -| `&` | `&` | `a & 0xFF` | -| `\|` | `\|` | `a \| b` | -| `^` | `^` | `a ^ b` | -| `~` | `~` | `~a` | -| `<<` | `<<` | `a << 2` | -| `>>` | `>>` | `a >> 1` | - -### 7.3 比較/論理演算子 - -| Cm | SV | 備考 | -|----|----|----| -| `==` | `==` | | -| `!=` | `!=` | | -| `<` | `<` | | -| `<=` | `<=` | 比較演算子(代入の `<=` とは異なる) | -| `>` | `>` | | -| `>=` | `>=` | | -| `&&` | `&&` | | -| `\|\|` | `\|\|` | | -| `!` | `~` | **暗黙変換**: 論理否定がビット反転に統合 | - -### 7.4 暗黙的な演算子変換 - -> **重要**: Cm の `!` (論理否定) は SV では `~` (ビット反転) にマッピングされます。 -> SV の `!` は 1 ビット論理否定ですが、現在のバックエンドは多ビット信号にも安全な `~` に統一しています。 - ---- - -## 8. 定数リテラルとビット幅 - -Cm の定数リテラルは、文脈のビット幅に合わせて **自動的にビット幅付きリテラル** に変換されます: - -| Cm リテラル | 文脈の型 | SV 出力 | -|------------|---------|---------| -| `true` | `bool` | `1'b1` | -| `false` | `bool` | `1'b0` | -| `42` | `uint` (32-bit) | `32'd42` | -| `42` | `utiny` (8-bit) | `8'd42` | -| `42` | `int` (符号付き32-bit) | `32'sd42` | -| `-5` | `int` | `-32'sd5` | - -### SV スタイルのリテラル - -Cm は SV スタイルのビット幅指定リテラルもそのまま使用可能: - -```cm -utiny mask = 8'b10101010; // → 8'b10101010 -ushort addr = 16'hFF00; // → 16'hFF00 -``` - -### 数値区切り文字 - -大きな数値にはアンダースコア `_` が使えます: - -```cm -const uint CLK_FREQ = 50_000_000; // → localparam CLK_FREQ = 32'd50000000; -``` - ---- - -## 9. 定数と localparam - -### `const` → `localparam` - -```cm -const uint CLK_FREQ = 27_000_000; -const uint CNT_MAX = CLK_FREQ / 2 - 1; -``` -```systemverilog -localparam CLK_FREQ = 32'd27000000; -localparam CNT_MAX = CLK_FREQ / 2 - 32'd1; -``` - -### `#[sv::param]` → `parameter` - -外部モジュールからオーバーライド可能なパラメータ: - -```cm -#[sv::param] const uint WIDTH = 8; -``` -```systemverilog -parameter WIDTH = 32'd8; -``` - ---- - -## 10. 制御構文 - -### 10.1 if / else if / else - -```cm -if (rst) { - counter = 0; -} else if (enable) { - counter = counter + 1; -} else { - // idle -} -``` -```systemverilog -if (rst) begin - counter <= 32'd0; -end else if (enable) begin - counter <= counter + 32'd1; -end else begin - // idle -end -``` - -### 10.2 switch → case - -```cm -switch (state) { - case 0: { - next_state = 1; - } - case 1: { - next_state = 2; - } - default: { - next_state = 0; - } -} -``` -```systemverilog -case (state) - 32'd0: begin - next_state <= 32'd1; - end - 32'd1: begin - next_state <= 32'd2; - end - default: begin - next_state <= 32'd0; - end -endcase -``` - ---- - -## 11. 連接と複製 - -### 連接 (Concatenation) - -```cm -result = {a, b}; // → result = {a, b}; -wide = {a, b, c}; // → wide = {a, b, c}; -``` - -### 複製 (Replication) - -```cm -replicated = {3{a}}; // → replicated = {3{a}}; -``` - -### ビルトイン関数 - -連接の `{...}` 構文がブロック `{...}` と曖昧になる場合、ビルトイン関数を使用: - -```cm -result = concat(a, b); // → result = {a, b}; -wide = replicate(nibble, 3); // → wide = {3{nibble}}; -``` - ---- - -## 12. 列挙型 (FSM) - -Cm の `enum` は SV の `typedef enum logic` に変換されます。 -ビット幅はバリアント数から自動計算: - -```cm -enum State { - IDLE, - RUN, - DONE, - ERROR -} -``` -```systemverilog -typedef enum logic [1:0] { - IDLE = 2'd0, - RUN = 2'd1, - DONE = 2'd2, - ERROR = 2'd3 -} State; -``` - -FSM での使用例: - -```cm -//! platform: sv - -enum State { IDLE, RUN, DONE } - -#[input] posedge clk; -#[input] bool rst = false; -#[output] uint count = 0; - -always void process(posedge clk) { - if (rst) { - count = 0; - } else { - switch (state) { - case State::IDLE: { state = State::RUN; } - case State::RUN: { count = count + 1; } - default: {} - } - } -} -``` - ---- - -## 13. SV 属性 - -### ポート属性 - -| 属性 | 効果 | 例 | -|------|------|----| -| `#[input]` | 入力ポート | `#[input] posedge clk;` | -| `#[output]` | 出力ポート | `#[output] utiny led = 0xFF;` | -| `#[inout]` | 双方向ポート | `#[inout] ushort bus;` | - -### パラメータ属性 - -| 属性 | 効果 | 例 | -|------|------|----| -| `#[sv::param]` | `parameter` 宣言 | `#[sv::param] uint WIDTH = 8;` | - -### メモリ属性 - -| 属性 | 効果 | 例 | -|------|------|----| -| `#[sv::bram]` | `(* ram_style = "block" *)` | `#[sv::bram] utiny mem[1024];` | -| `#[sv::lutram]` | `(* ram_style = "distributed" *)` | `#[sv::lutram] utiny lut[16];` | - -### 合成ヒント - -| 属性 | 効果 | -|------|------| -| `#[sv::pipeline]` | パイプラインヒントコメント生成 | -| `#[sv::share]` | リソース共有ヒントコメント生成 | - -### クロック/タイミング - -| 属性 | 効果 | 例 | -|------|------|----| -| `#[sv::clock_domain("name")]` | `async func` のクロックを指定 | `#[sv::clock_domain("fast")]` | - -### 物理配置 (XDC/CST 生成) - -| 属性 | 効果 | 例 | -|------|------|----| -| `#[sv::pin("A1")]` | ピン割り当て | `#[sv::pin("H11")] #[input] posedge clk;` | -| `#[sv::iostandard("LVCMOS33")]` | IO 電圧規格 | `#[sv::iostandard("LVCMOS18")]` | - ---- - -## 14. 暗黙的変換 - -Cm の SV バックエンドは、開発者が意識せずとも正しい SV コードを生成するために -多数の暗黙的変換を行います。 - -### 14.1 代入方式の自動決定 - -| Cm コード | 文脈 | SV 出力 | -|----------|------|---------| -| `x = expr;` | `always_ff` 内 | `x <= expr;` (ノンブロッキング) | -| `x = expr;` | `always_comb` 内 | `x = expr;` (ブロッキング) | - -### 14.2 論理否定の変換 - -| Cm コード | SV 出力 | 理由 | -|----------|---------|------| -| `!flag` | `~flag` | 多ビット信号に安全な `~` に統一 | -| `~data` | `~data` | そのまま | - -### 14.3 リテラルのビット幅付与 - -| Cm コード | 代入先の型 | SV 出力 | -|----------|-----------|---------| -| `counter = 0;` | `uint` (32-bit) | `counter <= 32'd0;` | -| `flag = true;` | `bool` (1-bit) | `flag <= 1'b1;` | -| `val = 42;` | `utiny` (8-bit) | `val <= 8'd42;` | - -### 14.4 クロック/リセットの自動追加 - -| 条件 | 動作 | -|------|------| -| `async func` 存在 & `clk` 未宣言 | `input logic clk` を自動追加 | -| `async func` 存在 & `rst` 未宣言 | `input logic rst` を `clk` の後に自動追加 | - -### 14.5 MIR 一時変数のインライン展開 - -MIR で生成される `_tXXXX` 一時変数は、SV 出力時に元の式にインライン展開されます: - -``` -// MIR: _t1000 = counter + 1; result = _t1000; -// SV: result <= counter + 32'd1; (一時変数が消える) -``` - -### 14.6 `self.` プレフィックスの除去 - -``` -// MIR: self.counter → SV: counter -``` - -### 14.7 `else if` の正規化 - -ネストした `else { if ... }` パターンは SV の `else if` に正規化されます: - -```systemverilog -// ネストせず、フラットな else if チェーンを生成 -if (cond1) begin - ... -end else if (cond2) begin - ... -end else begin - ... -end -``` - -### 14.8 冗長な三項演算子の除去 - -`cond ? x : x` のような冗長な三項演算子は単純な代入 `x` に最適化されます。 - ---- - -## 15. コンパイルと検証 - -### コンパイル - -```bash -# SV コード生成 -cm compile --target=sv blink.cm -o blink.sv - -# テストベンチも同時生成 -cm compile --target=sv blink.cm -o blink.sv --testbench -``` - -### Verilator でのシミュレーション - -```bash -# Verilator でコンパイル + シミュレーション -verilator --sv --lint-only blink.sv # 構文チェック -verilator --sv --cc blink.sv --exe # シミュレーション -``` - -### Icarus Verilog での検証 - -```bash -iverilog -g2012 -o blink_sim blink.sv blink_tb.sv -vvp blink_sim -``` - -### FPGA ビルド (Gowin EDA) - -```bash -# Cm → SV → Gowin EDA → ビットストリーム -cm compile --target=sv blink.cm -o blink.sv -gw_sh gowin_build.tcl -``` - ---- - -## 16. 全体例: カウンタ付き LED 点滅 - -```cm -//! platform: sv - -// === ポート定義 === -#[input] posedge clk; -#[input] negedge rst_n; -#[output] bool led = false; - -// === 定数 === -const uint CLK_FREQ = 27_000_000; // 27MHz (Tang Console) -const uint CNT_MAX = CLK_FREQ / 2 - 1; - -// === 内部レジスタ === -uint counter = 0; - -// === 順序回路: 非同期リセット付き === -always void blink(posedge clk, negedge rst_n) { - if (rst_n == false) { - counter = 0; - led = false; - } else { - if (counter == CNT_MAX) { - counter = 0; - led = !led; - } else { - counter = counter + 1; - } - } -} -``` - -生成される SV: -```systemverilog -`timescale 1ns / 1ps - -module blink ( - input logic clk, - input logic rst_n, - output logic led -); - localparam CLK_FREQ = 32'd27000000; - localparam CNT_MAX = CLK_FREQ / 2 - 32'd1; - - logic [31:0] counter; - - always_ff @(posedge clk or negedge rst_n) begin - if (rst_n == 1'b0) begin - counter <= 32'd0; - led <= 1'b0; - end else begin - if (counter == CNT_MAX) begin - counter <= 32'd0; - led <= ~led; - end else begin - counter <= counter + 32'd1; - end - end - end -endmodule -``` - ---- - -## 17. 付録: トークン・キーワード一覧 - -### SV 固有トークン - -| トークン | キーワード | TypeKind | 用途 | -|---------|---------|----------|------| -| `KwPosedge` | `posedge` | `Posedge` | 立ち上がりエッジ | -| `KwNegedge` | `negedge` | `Negedge` | 立ち下がりエッジ | -| `KwWire` | `wire` | `Wire` | ワイヤ修飾型 | -| `KwReg` | `reg` | `Reg` | レジスタ修飾型 | -| `KwAlways` | `always` | — | ロジックブロック修飾子 | -| `KwAssign` | `assign` | — | 連続代入文 | -| `KwInitial` | `initial` | — | シミュレーション初期化 | -| `KwBit` | `bit` | `Bit` | 任意ビット幅型 | - -### 既存トークンの SV での意味 - -| トークン | 通常 (LLVM) の意味 | SV での意味 | -|---------|-------------------|------------| -| `async` | JS 非同期関数 | `always_ff` ブロック生成 (後方互換) | -| `func` | 関数宣言 | `always_comb` ブロック生成 | -| `void` | 戻り値なし関数 | ブロック生成 (ff/comb) | -| `=` | 変数代入 | ff 内: `<=`, comb 内: `=` | -| `!` | 論理否定 | `~` (ビット反転に統合) | -| `const` | 定数宣言 | `localparam` | -| `switch/case` | パターンマッチ | `case/endcase` | -| `enum` | 列挙型 | `typedef enum logic` | - -### SV 予約語 (モジュール名回避) - -以下の名前はモジュール名として使用できません: - -``` -output, input, inout, module, wire, reg, logic, begin, end, -if, else, for, while, case, default, assign, always, initial, -posedge, negedge, task, function, parameter, integer, real, time, event -``` From 48aa5746452fed2b73a81a72369a273e0e995fd6 Mon Sep 17 00:00:00 2001 From: shadowlink0122 Date: Wed, 11 Mar 2026 22:04:30 +0900 Subject: [PATCH 09/12] =?UTF-8?q?=E4=BF=AE=E6=AD=A3:=20SV=20=E3=83=90?= =?UTF-8?q?=E3=83=83=E3=82=AF=E3=82=A8=E3=83=B3=E3=83=89=E3=81=AE=E8=A8=80?= =?UTF-8?q?=E8=AA=9E=E4=BB=95=E6=A7=98=E6=95=B4=E5=90=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit コンパイラ (codegen/sv/codegen.cpp): 1. const → 常にlocalparam(#[sv::param]属性があってもconstなら変更不可) - 評価順序を反転: const チェックを #[sv::param] チェックより先に - #[sv::param] + 非const → parameter(外部からオーバーライド可能) 2. void/非void → task/function の自然マッピング - 引数あり(edge paramなし)の非void関数 → function automatic - 引数あり(edge paramなし)のvoid関数 → task automatic - 引数なしvoid関数 → always_comb (後方互換維持) - #[sv::function]/#[sv::task] 属性は不要に ドキュメント: - tutorials/en/compiler/sv.md: parameter/function/task の説明修正 - tutorials/ja/compiler/sv.md: 同上 - docs/v0.15.1/sv_language_design.md: #[sv::param] const → 非const に修正 - docs/v0.15.1/sv_syntax_reference.md: parameter属性の説明修正 テスト: SVテスト 61/61 PASS、回帰なし --- docs/tutorials/en/compiler/sv.md | 31 +++++++++++++++--- docs/tutorials/ja/compiler/sv.md | 31 ++++++++++++++++-- docs/v0.15.1/sv_language_design.md | 4 +-- docs/v0.15.1/sv_syntax_reference.md | 2 +- src/codegen/sv/codegen.cpp | 49 +++++++++++++++-------------- 5 files changed, 85 insertions(+), 32 deletions(-) diff --git a/docs/tutorials/en/compiler/sv.md b/docs/tutorials/en/compiler/sv.md index e9167c9c..c6e0284d 100644 --- a/docs/tutorials/en/compiler/sv.md +++ b/docs/tutorials/en/compiler/sv.md @@ -306,13 +306,16 @@ localparam CLK_FREQ = 32'd27000000; localparam CNT_MAX = CLK_FREQ / 2 - 32'd1; ``` -### `#[sv::param]` → `parameter` +### `#[sv::param]` + non-`const` → `parameter` ```cm -#[sv::param] const uint WIDTH = 8; +#[sv::param] uint WIDTH = 8; // → parameter WIDTH = 32'd8; ``` +> **Note:** `const` always maps to `localparam` regardless of attributes. +> To get an overridable `parameter`, use `#[sv::param]` **without** `const`. + --- ## Control Flow @@ -354,9 +357,29 @@ case (state) endcase ``` ---- +### Functions and Tasks + +Functions with arguments (no edge params, no `always`/`async`) are automatically mapped based on return type: + +```cm +// Non-void → SV function +uint max_val(uint x, uint y) { + if (x > y) { return x; } + return y; +} +// → function automatic logic [31:0] max_val(...); ... endfunction + +// Void with args → SV task +void send_byte(utiny data) { + tx_valid = true; + tx_data = data; +} +// → task automatic send_byte(...); ... endtask +``` -## Concatenation and Replication +> **Note:** No `#[sv::function]` / `#[sv::task]` attributes needed — the compiler +> determines the mapping from the return type. Argument-less `void f()` still maps +> to `always_comb` for backward compatibility. ```cm result = {a, b}; // → {a, b} diff --git a/docs/tutorials/ja/compiler/sv.md b/docs/tutorials/ja/compiler/sv.md index 3216da57..6239f0cf 100644 --- a/docs/tutorials/ja/compiler/sv.md +++ b/docs/tutorials/ja/compiler/sv.md @@ -306,13 +306,16 @@ localparam CLK_FREQ = 32'd27000000; localparam CNT_MAX = CLK_FREQ / 2 - 32'd1; ``` -### `#[sv::param]` → `parameter` +### `#[sv::param]` + 非`const` → `parameter` ```cm -#[sv::param] const uint WIDTH = 8; +#[sv::param] uint WIDTH = 8; // → parameter WIDTH = 32'd8; ``` +> **注意:** `const` は属性に関わらず常に `localparam` にマッピングされます。 +> 外部からオーバーライド可能な `parameter` を得るには、`const` を付けずに `#[sv::param]` を使用してください。 + --- ## 制御構文 @@ -354,6 +357,30 @@ case (state) endcase ``` +### function と task + +引数あり(edgeパラメータなし)の関数は、戻り値の有無で自動的に振り分けられます: + +```cm +// 非void → SV function +uint max_val(uint x, uint y) { + if (x > y) { return x; } + return y; +} +// → function automatic logic [31:0] max_val(...); ... endfunction + +// 引数ありvoid → SV task +void send_byte(utiny data) { + tx_valid = true; + tx_data = data; +} +// → task automatic send_byte(...); ... endtask +``` + +> **注意:** `#[sv::function]` / `#[sv::task]` 属性は不要です。 +> コンパイラが戻り値の型から自動判定します。 +> 引数なし `void f()` は後方互換のため `always_comb` のままです。 + --- ## 連接と複製 diff --git a/docs/v0.15.1/sv_language_design.md b/docs/v0.15.1/sv_language_design.md index d98ac056..cc87a357 100644 --- a/docs/v0.15.1/sv_language_design.md +++ b/docs/v0.15.1/sv_language_design.md @@ -160,8 +160,8 @@ const uint CNT_MAX = CLK_FREQ / 2 - 1; // → localparam CLK_FREQ = 32'd50000000; // → localparam CNT_MAX = CLK_FREQ / 2 - 32'd1; -// #[sv::param] → parameter (外部から上書き可能) -#[sv::param] const uint WIDTH = 8; +// #[sv::param] + 非const → parameter (外部から上書き可能) +#[sv::param] uint WIDTH = 8; // → parameter WIDTH = 32'd8; ``` diff --git a/docs/v0.15.1/sv_syntax_reference.md b/docs/v0.15.1/sv_syntax_reference.md index e7cd0121..0faf303b 100644 --- a/docs/v0.15.1/sv_syntax_reference.md +++ b/docs/v0.15.1/sv_syntax_reference.md @@ -17,7 +17,7 @@ | `input logic [N:0] ` | `#[input]` 属性 | `input logic clk` | | `output logic [N:0] ` | `#[output]` 属性 | `output logic [7:0] led` | | `inout logic [N:0] ` | `#[inout]` 属性 | `inout logic [15:0] data` | -| `parameter = ;` | `#[sv::param]` 属性 | `parameter WIDTH = 8;` | +| `parameter = ;` | `#[sv::param]` 属性 (非`const`) | `parameter WIDTH = 8;` | --- diff --git a/src/codegen/sv/codegen.cpp b/src/codegen/sv/codegen.cpp index 96d9235a..46949c52 100644 --- a/src/codegen/sv/codegen.cpp +++ b/src/codegen/sv/codegen.cpp @@ -559,22 +559,27 @@ void SVCodeGen::analyzeFunction(const mir::MirFunction& func, SVModule& mod) { return; // 非always/非async関数 → SV function automatic または task automatic - // ただし、posedge/negedge以外の引数を持つ関数のみ - // 引数なし/posedge/negedge引数のみの関数はalwaysブロックとして出力 + // 引数あり(posedge/negedge以外)の関数は戻り値の有無で function/task に振り分け + // 引数なし関数 → always_comb / always_ff にフォールスルー(後方互換) if (!func.is_always && !func.is_async && func.always_kind == mir::MirFunction::AlwaysKind::None) { - // posedge/negedge以外の引数があるかチェック - bool has_sv_args = false; + // 引数の分類: edgeパラメータと通常パラメータを分離 + bool has_edge_param = false; + bool has_non_edge_args = false; for (auto arg_id : func.arg_locals) { if (arg_id < func.locals.size()) { auto& local = func.locals[arg_id]; - if (local.type && local.type->kind != hir::TypeKind::Posedge && - local.type->kind != hir::TypeKind::Negedge) { - has_sv_args = true; - break; + if (local.type && (local.type->kind == hir::TypeKind::Posedge || + local.type->kind == hir::TypeKind::Negedge)) { + has_edge_param = true; + } else if (local.type) { + has_non_edge_args = true; } } } - if (has_sv_args) { + // edgeパラメータなし、かつ通常引数がある → function/task + // edgeパラメータなし、引数なし → always_comb にフォールスルー(後方互換) + // edgeパラメータあり → always_ff にフォールスルー(後方互換) + if (!has_edge_param && (has_non_edge_args || !func.arg_locals.empty())) { std::ostringstream fn_ss; indent_level_ = 1; @@ -1846,22 +1851,9 @@ void SVCodeGen::analyzeMIR(const mir::MirProgram& program) { is_param = true; } - // parameter宣言(SVでは型なしが推奨: parameter NAME = VALUE;) - if (is_param) { - std::string param_decl = "parameter " + gv->name; - // 初期値がある場合は付加 - if (gv->init_value) { - param_decl += " = " + emitConstant(*gv->init_value, gv->type); - } - param_decl += ";"; - default_mod.parameters.push_back(param_decl); - continue; - } - - // const変数 → localparam宣言 + // const変数 → 常にlocalparam(#[sv::param]属性があってもconstなら変更不可) if (gv->is_const) { std::string localparam_decl = "localparam " + mapType(gv->type) + " " + gv->name; - // 初期値がある場合は付加 if (gv->init_value) { localparam_decl += " = " + emitConstant(*gv->init_value, gv->type); } @@ -1870,6 +1862,17 @@ void SVCodeGen::analyzeMIR(const mir::MirProgram& program) { continue; } + // #[sv::param] + 非const → parameter(外部からオーバーライド可能) + if (is_param) { + std::string param_decl = "parameter " + gv->name; + if (gv->init_value) { + param_decl += " = " + emitConstant(*gv->init_value, gv->type); + } + param_decl += ";"; + default_mod.parameters.push_back(param_decl); + continue; + } + // assign文 → wire宣言 + assign name = expr; if (gv->is_assign) { // wire宣言を追加 From b587720dfabee8dd23caa52e47c577bc7f4cee25 Mon Sep 17 00:00:00 2001 From: shadowlink0122 Date: Wed, 11 Mar 2026 22:15:50 +0900 Subject: [PATCH 10/12] =?UTF-8?q?=E4=BF=AE=E6=AD=A3:=20#[sv::param]?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E5=BB=83=E6=AD=A2=E3=80=81task=E5=87=BA?= =?UTF-8?q?=E5=8A=9B=E5=BB=83=E6=AD=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit コンパイラ (codegen/sv/codegen.cpp): 1. #[sv::param] → parameter のコードを完全削除 - const は常に localparam に変換(属性不要) - parameter 宣言は生成しない 2. task 出力を完全削除 - void関数は常に always_comb(引数あり/なし関わらず) - 非void関数(戻り値あり)のみ function automatic - task は合成に不適切なため廃止 テスト: - parameterized.cm: #[sv::param] → const に変更 - SVテスト 61/61 PASS、回帰なし ドキュメント: - tutorials/en/ja/compiler/sv.md: #[sv::param]・task 記述削除 - v0.15.1/sv_syntax_reference.md: parameter行をlocalparam行に修正 - v0.15.1/sv_language_design.md: 既に前コミットで修正済み --- docs/tutorials/en/compiler/sv.md | 29 ++++------------ docs/tutorials/ja/compiler/sv.md | 30 ++++------------ docs/v0.15.1/sv_syntax_reference.md | 2 +- src/codegen/sv/codegen.cpp | 54 ++++++++--------------------- tests/sv/advanced/parameterized.cm | 4 +-- 5 files changed, 32 insertions(+), 87 deletions(-) diff --git a/docs/tutorials/en/compiler/sv.md b/docs/tutorials/en/compiler/sv.md index c6e0284d..cef5bdc1 100644 --- a/docs/tutorials/en/compiler/sv.md +++ b/docs/tutorials/en/compiler/sv.md @@ -169,7 +169,7 @@ bit[26] counter; // → logic [25:0] counter #[inout] ushort bus; // → inout logic [15:0] bus // Parameters (overridable) -#[sv::param] uint WIDTH = 8; // → parameter WIDTH = 32'd8; +const uint WIDTH = 8; // → localparam logic [31:0] WIDTH = 32'd8; ``` --- @@ -306,15 +306,8 @@ localparam CLK_FREQ = 32'd27000000; localparam CNT_MAX = CLK_FREQ / 2 - 32'd1; ``` -### `#[sv::param]` + non-`const` → `parameter` - -```cm -#[sv::param] uint WIDTH = 8; -// → parameter WIDTH = 32'd8; -``` - -> **Note:** `const` always maps to `localparam` regardless of attributes. -> To get an overridable `parameter`, use `#[sv::param]` **without** `const`. +> **Note:** `const` always maps to `localparam`. There is no `parameter` generation. +> All compile-time constants become `localparam` in the SV output. --- @@ -359,7 +352,8 @@ endcase ### Functions and Tasks -Functions with arguments (no edge params, no `always`/`async`) are automatically mapped based on return type: +Functions with arguments (no edge params, no `always`/`async`) and a **non-void** return type +are automatically mapped to SV `function`: ```cm // Non-void → SV function @@ -368,18 +362,10 @@ uint max_val(uint x, uint y) { return y; } // → function automatic logic [31:0] max_val(...); ... endfunction - -// Void with args → SV task -void send_byte(utiny data) { - tx_valid = true; - tx_data = data; -} -// → task automatic send_byte(...); ... endtask ``` -> **Note:** No `#[sv::function]` / `#[sv::task]` attributes needed — the compiler -> determines the mapping from the return type. Argument-less `void f()` still maps -> to `always_comb` for backward compatibility. +> **Note:** `void` functions always map to `always_comb` blocks. +> Only non-void functions with return values become SV `function`. ```cm result = {a, b}; // → {a, b} @@ -417,7 +403,6 @@ typedef enum logic [1:0] { | `#[input]` | Input port | `#[input] posedge clk;` | | `#[output]` | Output port | `#[output] utiny led = 0xFF;` | | `#[inout]` | Bidirectional port | `#[inout] ushort bus;` | -| `#[sv::param]` | `parameter` declaration | `#[sv::param] uint WIDTH = 8;` | | `#[sv::bram]` | `(* ram_style = "block" *)` | `#[sv::bram] utiny mem[1024];` | | `#[sv::lutram]` | `(* ram_style = "distributed" *)` | `#[sv::lutram] utiny lut[16];` | | `#[sv::clock_domain("name")]` | Clock for `async func` | `#[sv::clock_domain("fast")]` | diff --git a/docs/tutorials/ja/compiler/sv.md b/docs/tutorials/ja/compiler/sv.md index 6239f0cf..b5544428 100644 --- a/docs/tutorials/ja/compiler/sv.md +++ b/docs/tutorials/ja/compiler/sv.md @@ -168,8 +168,8 @@ bit[26] counter; // → logic [25:0] counter // 双方向ポート #[inout] ushort bus; // → inout logic [15:0] bus -// パラメータ(外部から上書き可能) -#[sv::param] uint WIDTH = 8; // → parameter WIDTH = 32'd8; +// パラメータ(定数) +const uint WIDTH = 8; // → localparam logic [31:0] WIDTH = 32'd8; ``` --- @@ -306,15 +306,8 @@ localparam CLK_FREQ = 32'd27000000; localparam CNT_MAX = CLK_FREQ / 2 - 32'd1; ``` -### `#[sv::param]` + 非`const` → `parameter` - -```cm -#[sv::param] uint WIDTH = 8; -// → parameter WIDTH = 32'd8; -``` - -> **注意:** `const` は属性に関わらず常に `localparam` にマッピングされます。 -> 外部からオーバーライド可能な `parameter` を得るには、`const` を付けずに `#[sv::param]` を使用してください。 +> **注意:** `const` は常に `localparam` にマッピングされます。 +> `parameter` は生成されません。コンパイル時定数は全て `localparam` になります。 --- @@ -359,7 +352,7 @@ endcase ### function と task -引数あり(edgeパラメータなし)の関数は、戻り値の有無で自動的に振り分けられます: +引数あり(edgeパラメータなし)かつ **非void(戻り値あり)** の関数は、自動的に SV `function` に変換されます: ```cm // 非void → SV function @@ -368,18 +361,10 @@ uint max_val(uint x, uint y) { return y; } // → function automatic logic [31:0] max_val(...); ... endfunction - -// 引数ありvoid → SV task -void send_byte(utiny data) { - tx_valid = true; - tx_data = data; -} -// → task automatic send_byte(...); ... endtask ``` -> **注意:** `#[sv::function]` / `#[sv::task]` 属性は不要です。 -> コンパイラが戻り値の型から自動判定します。 -> 引数なし `void f()` は後方互換のため `always_comb` のままです。 +> **注意:** `void` 関数は常に `always_comb` ブロックになります。 +> 戻り値がある非void関数のみが SV `function` になります。 --- @@ -421,7 +406,6 @@ typedef enum logic [1:0] { | `#[input]` | 入力ポート | `#[input] posedge clk;` | | `#[output]` | 出力ポート | `#[output] utiny led = 0xFF;` | | `#[inout]` | 双方向ポート | `#[inout] ushort bus;` | -| `#[sv::param]` | `parameter`宣言 | `#[sv::param] uint WIDTH = 8;` | | `#[sv::bram]` | `(* ram_style = "block" *)` | `#[sv::bram] utiny mem[1024];` | | `#[sv::lutram]` | `(* ram_style = "distributed" *)` | `#[sv::lutram] utiny lut[16];` | | `#[sv::clock_domain("name")]` | `async func`のクロック指定 | `#[sv::clock_domain("fast")]` | diff --git a/docs/v0.15.1/sv_syntax_reference.md b/docs/v0.15.1/sv_syntax_reference.md index 0faf303b..3111fd37 100644 --- a/docs/v0.15.1/sv_syntax_reference.md +++ b/docs/v0.15.1/sv_syntax_reference.md @@ -17,7 +17,7 @@ | `input logic [N:0] ` | `#[input]` 属性 | `input logic clk` | | `output logic [N:0] ` | `#[output]` 属性 | `output logic [7:0] led` | | `inout logic [N:0] ` | `#[inout]` 属性 | `inout logic [15:0] data` | -| `parameter = ;` | `#[sv::param]` 属性 (非`const`) | `parameter WIDTH = 8;` | +| `localparam = ;` | `const` 宣言 | `localparam logic [31:0] WIDTH = 32'd8;` | --- diff --git a/src/codegen/sv/codegen.cpp b/src/codegen/sv/codegen.cpp index 46949c52..59ee6991 100644 --- a/src/codegen/sv/codegen.cpp +++ b/src/codegen/sv/codegen.cpp @@ -558,11 +558,10 @@ void SVCodeGen::analyzeFunction(const mir::MirFunction& func, SVModule& mod) { if (func.name == "main") return; - // 非always/非async関数 → SV function automatic または task automatic - // 引数あり(posedge/negedge以外)の関数は戻り値の有無で function/task に振り分け - // 引数なし関数 → always_comb / always_ff にフォールスルー(後方互換) + // 非always/非async関数で、非void(戻り値あり)の場合 → SV function automatic + // void関数は always_comb / always_ff にフォールスルー if (!func.is_always && !func.is_async && func.always_kind == mir::MirFunction::AlwaysKind::None) { - // 引数の分類: edgeパラメータと通常パラメータを分離 + // edgeパラメータの有無を確認 bool has_edge_param = false; bool has_non_edge_args = false; for (auto arg_id : func.arg_locals) { @@ -576,14 +575,8 @@ void SVCodeGen::analyzeFunction(const mir::MirFunction& func, SVModule& mod) { } } } - // edgeパラメータなし、かつ通常引数がある → function/task - // edgeパラメータなし、引数なし → always_comb にフォールスルー(後方互換) - // edgeパラメータあり → always_ff にフォールスルー(後方互換) - if (!has_edge_param && (has_non_edge_args || !func.arg_locals.empty())) { - std::ostringstream fn_ss; - indent_level_ = 1; - // 戻り値型を取得 + // 非void関数(戻り値あり)→ SV function automatic bool is_void = true; std::string ret_type_str = "void"; if (func.return_local < func.locals.size()) { @@ -594,6 +587,10 @@ void SVCodeGen::analyzeFunction(const mir::MirFunction& func, SVModule& mod) { } } + if (!is_void && !has_edge_param) { + std::ostringstream fn_ss; + indent_level_ = 1; + // 引数リスト構築(posedge/negedge型を除外) std::vector args; for (auto arg_id : func.arg_locals) { @@ -606,11 +603,7 @@ void SVCodeGen::analyzeFunction(const mir::MirFunction& func, SVModule& mod) { } } - if (is_void) { - fn_ss << indent() << "task automatic " << func.name << "("; - } else { - fn_ss << indent() << "function automatic " << ret_type_str << " " << func.name << "("; - } + fn_ss << indent() << "function automatic " << ret_type_str << " " << func.name << "("; for (size_t i = 0; i < args.size(); ++i) { if (i > 0) fn_ss << ", "; fn_ss << args[i]; @@ -621,11 +614,11 @@ void SVCodeGen::analyzeFunction(const mir::MirFunction& func, SVModule& mod) { increaseIndent(); std::set arg_set(func.arg_locals.begin(), func.arg_locals.end()); for (size_t i = 0; i < func.locals.size(); ++i) { - if (i == func.return_local) continue; // 戻り値 - if (arg_set.count(static_cast(i))) continue; // 引数 + if (i == func.return_local) continue; + if (arg_set.count(static_cast(i))) continue; auto& local = func.locals[i]; if (local.name.empty() || local.name.find('@') != std::string::npos) continue; - // ポインタ型テンポラリはスキップ(__builtin_* Call引数用) + // ポインタ型テンポラリはスキップ if (local.name.find("_t") == 0 && local.type && local.type->kind == hir::TypeKind::Pointer) continue; fn_ss << indent() << mapType(local.type) << " " << local.name << ";\n"; @@ -636,7 +629,6 @@ void SVCodeGen::analyzeFunction(const mir::MirFunction& func, SVModule& mod) { std::set visited; std::ostringstream body_ss; emitBlockRecursive(func, 0, visited, body_ss); - // @return → return に置換 std::string body = body_ss.str(); size_t pos = 0; while ((pos = body.find("@return", pos)) != std::string::npos) { @@ -647,16 +639,11 @@ void SVCodeGen::analyzeFunction(const mir::MirFunction& func, SVModule& mod) { } decreaseIndent(); - - if (is_void) { - fn_ss << indent() << "endtask\n"; - } else { - fn_ss << indent() << "endfunction\n"; - } + fn_ss << indent() << "endfunction\n"; mod.function_blocks.push_back(fn_ss.str()); return; - } // if (has_sv_args) + } // if (!is_void && !has_edge_param) } // ローカル変数を内部ワイヤ/レジスタとして宣言 @@ -1851,7 +1838,7 @@ void SVCodeGen::analyzeMIR(const mir::MirProgram& program) { is_param = true; } - // const変数 → 常にlocalparam(#[sv::param]属性があってもconstなら変更不可) + // const変数 → 常にlocalparam if (gv->is_const) { std::string localparam_decl = "localparam " + mapType(gv->type) + " " + gv->name; if (gv->init_value) { @@ -1862,17 +1849,6 @@ void SVCodeGen::analyzeMIR(const mir::MirProgram& program) { continue; } - // #[sv::param] + 非const → parameter(外部からオーバーライド可能) - if (is_param) { - std::string param_decl = "parameter " + gv->name; - if (gv->init_value) { - param_decl += " = " + emitConstant(*gv->init_value, gv->type); - } - param_decl += ";"; - default_mod.parameters.push_back(param_decl); - continue; - } - // assign文 → wire宣言 + assign name = expr; if (gv->is_assign) { // wire宣言を追加 diff --git a/tests/sv/advanced/parameterized.cm b/tests/sv/advanced/parameterized.cm index 687122f2..76c958b3 100644 --- a/tests/sv/advanced/parameterized.cm +++ b/tests/sv/advanced/parameterized.cm @@ -4,9 +4,9 @@ //! test: a=10, b=20, c=30, sel=2 -> result=30 // パラメータ化モジュールテスト -// sv::param アトリビュートでパラメータ宣言 +// const宣言はlocalparamとして出力される -#[sv::param] int WIDTH = 32; +const int WIDTH = 32; #[input] int a = 0; #[input] int b = 0; From e0a757236c0a5f24e525d664b9646c9d55472e05 Mon Sep 17 00:00:00 2001 From: shadowlink0122 Date: Wed, 11 Mar 2026 22:51:30 +0900 Subject: [PATCH 11/12] =?UTF-8?q?=E4=BF=AE=E6=AD=A3:=20SV=20=E3=83=90?= =?UTF-8?q?=E3=83=83=E3=82=AF=E3=82=A8=E3=83=B3=E3=83=89=E5=93=81=E8=B3=AA?= =?UTF-8?q?=E6=94=B9=E5=96=846=E9=A0=85=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit コンパイラ (codegen/sv/codegen.cpp): 1. function内MIRテンポラリ変数のインライン展開 - always ブロックと同じ2パス処理を function にも適用 - _tXXXX 変数が除去されクリーンな出力に 2. 一般function呼び出しのSV出力を実装 - result = func_name(arg1, arg2); として出力 - always_comb/always_ff 内での関数呼び出しが可能に 3. else if 正規化後のインデント修正 - 結合時にブロック内のインデントを4スペース浅く調整 - 余分なend除去も統合した新ロジック ドキュメント (tutorials/en,ja/compiler/sv.md): 4. 数値区切り文字の記述を削除(未サポート) 5. localparam出力例に型情報 logic [31:0] を追加 テスト: SVテスト 61/61 PASS、回帰なし --- docs/tutorials/en/compiler/sv.md | 12 +- docs/tutorials/ja/compiler/sv.md | 10 +- src/codegen/sv/codegen.cpp | 295 ++++++++++++++++++++++--------- 3 files changed, 225 insertions(+), 92 deletions(-) diff --git a/docs/tutorials/en/compiler/sv.md b/docs/tutorials/en/compiler/sv.md index cef5bdc1..8002fcce 100644 --- a/docs/tutorials/en/compiler/sv.md +++ b/docs/tutorials/en/compiler/sv.md @@ -285,10 +285,8 @@ utiny mask = 8'b10101010; // → 8'b10101010 ushort addr = 16'hFF00; // → 16'hFF00 ``` -### Numeric Separators - ```cm -const uint CLK_FREQ = 50_000_000; // → localparam CLK_FREQ = 32'd50000000; +const uint CLK_FREQ = 50000000; // → localparam logic [31:0] CLK_FREQ = 32'd50000000; ``` --- @@ -298,12 +296,12 @@ const uint CLK_FREQ = 50_000_000; // → localparam CLK_FREQ = 32'd50000000; ### `const` → `localparam` ```cm -const uint CLK_FREQ = 27_000_000; +const uint CLK_FREQ = 27000000; const uint CNT_MAX = CLK_FREQ / 2 - 1; ``` ```systemverilog -localparam CLK_FREQ = 32'd27000000; -localparam CNT_MAX = CLK_FREQ / 2 - 32'd1; +localparam logic [31:0] CLK_FREQ = 32'd27000000; +localparam logic [31:0] CNT_MAX = CLK_FREQ / 2 - 32'd1; ``` > **Note:** `const` always maps to `localparam`. There is no `parameter` generation. @@ -504,7 +502,7 @@ gw_sh gowin_build.tcl #[input] negedge rst_n; #[output] bool led = false; -const uint CLK_FREQ = 27_000_000; +const uint CLK_FREQ = 27000000; const uint CNT_MAX = CLK_FREQ / 2 - 1; uint counter = 0; diff --git a/docs/tutorials/ja/compiler/sv.md b/docs/tutorials/ja/compiler/sv.md index b5544428..1e354a18 100644 --- a/docs/tutorials/ja/compiler/sv.md +++ b/docs/tutorials/ja/compiler/sv.md @@ -285,10 +285,8 @@ utiny mask = 8'b10101010; // → 8'b10101010 ushort addr = 16'hFF00; // → 16'hFF00 ``` -### 数値区切り文字 - ```cm -const uint CLK_FREQ = 50_000_000; // → localparam CLK_FREQ = 32'd50000000; +const uint CLK_FREQ = 50000000; // → localparam logic [31:0] CLK_FREQ = 32'd50000000; ``` --- @@ -302,8 +300,8 @@ const uint CLK_FREQ = 27_000_000; const uint CNT_MAX = CLK_FREQ / 2 - 1; ``` ```systemverilog -localparam CLK_FREQ = 32'd27000000; -localparam CNT_MAX = CLK_FREQ / 2 - 32'd1; +localparam logic [31:0] CLK_FREQ = 32'd27000000; +localparam logic [31:0] CNT_MAX = CLK_FREQ / 2 - 32'd1; ``` > **注意:** `const` は常に `localparam` にマッピングされます。 @@ -507,7 +505,7 @@ gw_sh gowin_build.tcl #[input] negedge rst_n; #[output] bool led = false; -const uint CLK_FREQ = 27_000_000; +const uint CLK_FREQ = 27000000; const uint CNT_MAX = CLK_FREQ / 2 - 1; uint counter = 0; diff --git a/src/codegen/sv/codegen.cpp b/src/codegen/sv/codegen.cpp index 59ee6991..2fb65337 100644 --- a/src/codegen/sv/codegen.cpp +++ b/src/codegen/sv/codegen.cpp @@ -610,9 +610,11 @@ void SVCodeGen::analyzeFunction(const mir::MirFunction& func, SVModule& mod) { } fn_ss << ");\n"; - // ローカル変数宣言(引数と戻り値を除く) + // ローカル変数宣言(引数と戻り値を除く、テンポラリ変数は後で除去) increaseIndent(); std::set arg_set(func.arg_locals.begin(), func.arg_locals.end()); + // 一旦全ローカル変数を記録(テンポラリは後でスキップ判定) + std::vector> local_decls; for (size_t i = 0; i < func.locals.size(); ++i) { if (i == func.return_local) continue; if (arg_set.count(static_cast(i))) continue; @@ -621,23 +623,145 @@ void SVCodeGen::analyzeFunction(const mir::MirFunction& func, SVModule& mod) { // ポインタ型テンポラリはスキップ if (local.name.find("_t") == 0 && local.type && local.type->kind == hir::TypeKind::Pointer) continue; - fn_ss << indent() << mapType(local.type) << " " << local.name << ";\n"; + local_decls.push_back({i, mapType(local.type) + " " + local.name + ";"}); } - // 関数本体 + // 関数本体 — テンポラリ変数のインライン展開 + std::string body_content; if (!func.basic_blocks.empty() && func.basic_blocks[0]) { std::set visited; std::ostringstream body_ss; emitBlockRecursive(func, 0, visited, body_ss); - std::string body = body_ss.str(); + std::string raw_body = body_ss.str(); + + // @return → 関数名 に置換 size_t pos = 0; - while ((pos = body.find("@return", pos)) != std::string::npos) { - body.replace(pos, 7, func.name); + while ((pos = raw_body.find("@return", pos)) != std::string::npos) { + raw_body.replace(pos, 7, func.name); pos += func.name.size(); } - fn_ss << body; + + // テンポラリ変数のインライン展開(always ブロックと同じロジック) + std::istringstream raw_stream(raw_body); + std::string line; + std::vector lines; + while (std::getline(raw_stream, line)) { + lines.push_back(line); + } + + // Pass 1: テンポラリ変数の値を収集 + std::map fn_temp_values; + for (const auto& l : lines) { + std::string tr = l; + size_t start = tr.find_first_not_of(' '); + if (start == std::string::npos) continue; + tr = tr.substr(start); + if (tr.size() > 2 && tr[0] == '_' && tr[1] == 't' && std::isdigit(tr[2])) { + auto eq_pos = tr.find(" = "); + if (eq_pos != std::string::npos) { + std::string var_name = tr.substr(0, eq_pos); + std::string value = tr.substr(eq_pos + 3); + if (!value.empty() && value.back() == ';') value.pop_back(); + fn_temp_values[var_name] = value; + } + } + } + + // テンポラリ変数を再帰的に展開するラムダ + auto fn_inline_temps = [&fn_temp_values](const std::string& expr) -> std::string { + std::string result = expr; + for (int iter = 0; iter < 10; ++iter) { + bool changed = false; + for (const auto& [var, val] : fn_temp_values) { + size_t p = 0; + while ((p = result.find(var, p)) != std::string::npos) { + bool at_start = (p == 0 || (!std::isalnum(result[p - 1]) && result[p - 1] != '_')); + bool at_end = (p + var.size() >= result.size() || + (!std::isalnum(result[p + var.size()]) && result[p + var.size()] != '_')); + if (at_start && at_end) { + std::string replacement = val; + if (val.find(' ') != std::string::npos) { + bool is_full_rhs = (p == 0 && p + var.size() == result.size()); + if (!is_full_rhs) replacement = "(" + val + ")"; + } + result.replace(p, var.size(), replacement); + changed = true; + p += replacement.size(); + } else { p += var.size(); } + } + } + if (!changed) break; + } + return result; + }; + + // Pass 2: テンポラリ代入行をスキップし、残りの文をインライン展開 + std::ostringstream expanded_ss; + for (const auto& l : lines) { + std::string tr = l; + size_t start = tr.find_first_not_of(' '); + if (start == std::string::npos) { expanded_ss << l << "\n"; continue; } + std::string content = tr.substr(start); + // テンポラリ代入行はスキップ + if (content.size() > 2 && content[0] == '_' && content[1] == 't' && + std::isdigit(content[2]) && content.find(" = ") != std::string::npos) { + continue; + } + // 代入文のインライン展開 + std::string line_indent = l.substr(0, start); + auto eq_pos = content.find(" = "); + if (eq_pos != std::string::npos) { + std::string lhs = content.substr(0, eq_pos); + std::string rhs = content.substr(eq_pos + 3); + if (!rhs.empty() && rhs.back() == ';') rhs.pop_back(); + rhs = fn_inline_temps(rhs); + expanded_ss << line_indent << lhs << " = " << rhs << ";\n"; + } else { + // if/else等の制御文でもテンポラリをインライン展開 + std::string expanded = l; + for (int iter = 0; iter < 10; ++iter) { + bool changed = false; + for (const auto& [var, val] : fn_temp_values) { + size_t p = 0; + while ((p = expanded.find(var, p)) != std::string::npos) { + bool at_start = (p == 0 || (!std::isalnum(expanded[p - 1]) && expanded[p - 1] != '_')); + bool at_end = (p + var.size() >= expanded.size() || + (!std::isalnum(expanded[p + var.size()]) && expanded[p + var.size()] != '_')); + if (at_start && at_end) { + expanded.replace(p, var.size(), val); + p += val.size(); + changed = true; + } else { p += var.size(); } + } + } + if (!changed) break; + } + expanded_ss << expanded << "\n"; + } + } + body_content = expanded_ss.str(); + + // テンポラリ変数のローカル宣言をスキップ + auto decl_it = local_decls.begin(); + while (decl_it != local_decls.end()) { + auto& local = func.locals[decl_it->first]; + if (local.name.size() > 2 && local.name[0] == '_' && local.name[1] == 't' && + std::isdigit(local.name[2]) && fn_temp_values.count(local.name)) { + decl_it = local_decls.erase(decl_it); + } else { + ++decl_it; + } + } + } + + // ローカル変数宣言を出力 + for (const auto& decl : local_decls) { + fn_ss << indent() << decl.second << "\n"; } + // 展開済みの関数本体を出力 + fn_ss << body_content; + decreaseIndent(); fn_ss << indent() << "endfunction\n"; @@ -1165,7 +1289,7 @@ void SVCodeGen::analyzeFunction(const mir::MirFunction& func, SVModule& mod) { } // else if 正規化: "end else begin\n if (...) begin" → "end else if (...) begin" - // 余分な末尾endも同時に除去 + // 結合時にブロック内容のインデントを1レベル浅く調整し、余分なendも除去 { std::istringstream elif_stream(block_content); std::vector elif_lines; @@ -1174,101 +1298,70 @@ void SVCodeGen::analyzeFunction(const mir::MirFunction& func, SVModule& mod) { elif_lines.push_back(elif_line); } - std::vector elif_result; - std::vector extra_end_positions; // 除去すべきendのインデックス + std::ostringstream elif_ss; + bool first = true; + // インデント調整量のスタック: 結合されたelse ifの中で4スペース浅くする + int indent_adjust = 0; + std::vector adjust_stack; // begin/endの対応でadjustを追跡 for (size_t i = 0; i < elif_lines.size(); ++i) { auto trim_start = elif_lines[i].find_first_not_of(' '); if (trim_start == std::string::npos) { - elif_result.push_back(elif_lines[i]); + if (!first) elif_ss << "\n"; + elif_ss << elif_lines[i]; + first = false; continue; } std::string trimmed = elif_lines[i].substr(trim_start); std::string indent_str = elif_lines[i].substr(0, trim_start); - // "end else begin" パターン検出 + // "end else begin" + 次行 "if (...)" パターン検出 if (trimmed == "end else begin" && i + 1 < elif_lines.size()) { auto next_trim = elif_lines[i + 1].find_first_not_of(' '); if (next_trim != std::string::npos && elif_lines[i + 1].substr(next_trim, 4) == "if (") { - // "end else begin\n if (" → "end else if (" - elif_result.push_back(indent_str + "end else " + - elif_lines[i + 1].substr(next_trim)); + // 結合: "end else if (...) begin" + if (!first) elif_ss << "\n"; + elif_ss << indent_str << "end else " << elif_lines[i + 1].substr(next_trim); + first = false; ++i; // if行をスキップ - - // 対応する余分なendを探す: begin/endの対応を追跡 - int depth = 0; - for (size_t j = i + 1; j < elif_lines.size(); ++j) { - auto jt = elif_lines[j].find_first_not_of(' '); - if (jt == std::string::npos) - continue; - std::string jc = elif_lines[j].substr(jt); - // beginを含む行でdepth++ - if (jc.find("begin") != std::string::npos && - jc.find("begin") == jc.size() - 5) - depth++; - // "end"で始まる行でdepth-- - if (jc == "end" || jc.substr(0, 4) == "end ") { - if (depth > 0) { - depth--; - } else { - // このendが余分:マーク - extra_end_positions.push_back(j); - break; - } - } - } + // 次行以降のインデントを4スペース浅く調整 + indent_adjust += 4; + // 対応するendを見つけるためにdepthカウンタを初期化 + adjust_stack.push_back(0); continue; } } - elif_result.push_back(elif_lines[i]); - } - // 余分なend行を除去して最終結果を構築 - if (!extra_end_positions.empty()) { - std::set skip_set(extra_end_positions.begin(), extra_end_positions.end()); - // elif_resultは既に変換済みなので、元のelif_linesからの対応が必要 - // 代わりに直接elif_linesベースで再構築 - std::ostringstream elif_ss; - bool first = true; - for (size_t i = 0; i < elif_lines.size(); ++i) { - if (skip_set.count(i)) - continue; - auto trim_start = elif_lines[i].find_first_not_of(' '); - std::string trimmed = - (trim_start != std::string::npos) ? elif_lines[i].substr(trim_start) : ""; - std::string indent_str = - (trim_start != std::string::npos) ? elif_lines[i].substr(0, trim_start) : ""; - - // else begin + 次行if の変換 - if (trimmed == "end else begin" && i + 1 < elif_lines.size()) { - auto next_trim = elif_lines[i + 1].find_first_not_of(' '); - if (next_trim != std::string::npos && - elif_lines[i + 1].substr(next_trim, 4) == "if (") { - if (!first) - elif_ss << "\n"; - elif_ss << indent_str << "end else " << elif_lines[i + 1].substr(next_trim); - first = false; - ++i; + // インデント調整中: begin/endの深さを追跡 + if (indent_adjust > 0 && !adjust_stack.empty()) { + // beginを含む行でdepth++ + if (trimmed.size() >= 5 && trimmed.substr(trimmed.size() - 5) == "begin") { + adjust_stack.back()++; + } + // "end"で始まる行でdepth-- + if (trimmed == "end" || (trimmed.size() >= 4 && trimmed.substr(0, 4) == "end ")) { + if (adjust_stack.back() > 0) { + adjust_stack.back()--; + } else { + // この"end"は余分(結合されたelse ifの対応end)→ スキップ + indent_adjust -= 4; + adjust_stack.pop_back(); continue; } } - if (!first) - elif_ss << "\n"; - elif_ss << elif_lines[i]; - first = false; } - block_content = elif_ss.str(); - } else if (!elif_result.empty()) { - // 変換があったがextra_endなし → elif_resultを使用 - std::ostringstream elif_ss; - for (size_t i = 0; i < elif_result.size(); ++i) { - if (i > 0) - elif_ss << "\n"; - elif_ss << elif_result[i]; + + // インデント調整を適用 + if (!first) elif_ss << "\n"; + if (indent_adjust > 0 && static_cast(trim_start) > indent_adjust) { + elif_ss << indent_str.substr(indent_adjust) << trimmed; + } else { + elif_ss << elif_lines[i]; } - block_content = elif_ss.str(); + first = false; } + block_content = elif_ss.str(); } // 冗長三項演算子除去: "cond ? X : X" → "X" @@ -1675,6 +1768,50 @@ void SVCodeGen::emitTerminator(const mir::MirTerminator& term, const mir::MirFun } // 成功ブロックに続行 emitBlockRecursive(func, cd.success, visited, ss, merge_block); + } else { + // 一般的な関数呼び出し: result = func_name(arg1, arg2, ...); + // ノンブロッキング代入の判定 + bool use_nb = func.is_async; + if (!use_nb) { + for (const auto& local : func.locals) { + if (local.type && (local.type->kind == hir::TypeKind::Posedge || + local.type->kind == hir::TypeKind::Negedge)) { + use_nb = true; + break; + } + } + } + + // 引数リスト構築 + std::string args_str; + for (size_t i = 0; i < cd.args.size(); ++i) { + if (i > 0) args_str += ", "; + if (cd.args[i]) { + if (cd.args[i]->kind == mir::MirOperand::Move || + cd.args[i]->kind == mir::MirOperand::Copy) { + const auto& place = std::get(cd.args[i]->data); + args_str += emitPlace(place, func); + } else if (cd.args[i]->kind == mir::MirOperand::Constant) { + args_str += emitConstant( + std::get(cd.args[i]->data), + cd.args[i]->type); + } else { + args_str += "0"; + } + } + } + + // 戻り値がある場合は代入文として出力 + if (cd.destination) { + std::string lhs = emitPlace(*cd.destination, func); + ss << indent() << lhs << (use_nb ? " <= " : " = ") + << func_name << "(" << args_str << ");\n"; + } else { + // void関数呼び出し(taskの場合等) + ss << indent() << func_name << "(" << args_str << ");\n"; + } + // 成功ブロックに続行 + emitBlockRecursive(func, cd.success, visited, ss, merge_block); } // その他の関数呼び出しはスキップ break; From 15a3eb0051fff2616412a139275ad50871b438f8 Mon Sep 17 00:00:00 2001 From: shadowlink0122 Date: Wed, 11 Mar 2026 22:56:52 +0900 Subject: [PATCH 12/12] =?UTF-8?q?=E4=BF=AE=E6=AD=A3:=20switch/case?= =?UTF-8?q?=E6=A7=8B=E6=96=87=E3=83=89=E3=82=AD=E3=83=A5=E3=83=A1=E3=83=B3?= =?UTF-8?q?=E3=83=88=E4=BF=AE=E6=AD=A3=E3=81=A8enum=20FSM=E4=BE=8B?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ドキュメント (tutorials/en,ja/compiler/sv.md): - switch/case構文を正しい case(pattern) 形式に修正 - 旧: case 0: { ... } / default: { ... } - 新: case(0) { ... } / else { ... } - enum + switch FSMの使用例を追加 - case(State::IDLE) { ... } 形式でenum マッチ テスト: SVテスト 61/61 PASS --- docs/tutorials/en/compiler/sv.md | 25 ++++++++++++++++++++++--- docs/tutorials/ja/compiler/sv.md | 25 ++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/docs/tutorials/en/compiler/sv.md b/docs/tutorials/en/compiler/sv.md index 8002fcce..9340fe90 100644 --- a/docs/tutorials/en/compiler/sv.md +++ b/docs/tutorials/en/compiler/sv.md @@ -335,9 +335,9 @@ end ```cm switch (state) { - case 0: { next_state = 1; } - case 1: { next_state = 2; } - default: { next_state = 0; } + case(0) { next_state = 1; } + case(1) { next_state = 2; } + else { next_state = 0; } } ``` ```systemverilog @@ -348,6 +348,9 @@ case (state) endcase ``` +> **Note:** Cm switch syntax is `case(pattern) { ... }` with parentheses. +> Use `else { ... }` for the default case. + ### Functions and Tasks Functions with arguments (no edge params, no `always`/`async`) and a **non-void** return type @@ -392,6 +395,22 @@ typedef enum logic [1:0] { } State; ``` +### enum + switch (FSM) + +Enum variants can be matched with `case(EnumType::Variant)`: + +```cm +State current = State::IDLE; + +void fsm(posedge clk) { + switch (current) { + case(State::IDLE) { current = State::RUN; } + case(State::RUN) { current = State::DONE; } + else { current = State::IDLE; } + } +} +``` + --- ## SV Attributes diff --git a/docs/tutorials/ja/compiler/sv.md b/docs/tutorials/ja/compiler/sv.md index 1e354a18..8a8255de 100644 --- a/docs/tutorials/ja/compiler/sv.md +++ b/docs/tutorials/ja/compiler/sv.md @@ -335,9 +335,9 @@ end ```cm switch (state) { - case 0: { next_state = 1; } - case 1: { next_state = 2; } - default: { next_state = 0; } + case(0) { next_state = 1; } + case(1) { next_state = 2; } + else { next_state = 0; } } ``` ```systemverilog @@ -348,6 +348,9 @@ case (state) endcase ``` +> **注意:** Cmの switch 構文は `case(パターン) { ... }` 形式です。 +> デフォルトは `else { ... }` で記述します。 + ### function と task 引数あり(edgeパラメータなし)かつ **非void(戻り値あり)** の関数は、自動的に SV `function` に変換されます: @@ -395,6 +398,22 @@ typedef enum logic [1:0] { } State; ``` +### enum + switch (FSM) + +enum バリアントは `case(EnumType::Variant)` でマッチできます: + +```cm +State current = State::IDLE; + +void fsm(posedge clk) { + switch (current) { + case(State::IDLE) { current = State::RUN; } + case(State::RUN) { current = State::DONE; } + else { current = State::IDLE; } + } +} +``` + --- ## SV属性