Skip to content

Commit bb53f52

Browse files
authored
Merge pull request #1301 from veryl-lang/add_sim
Add veryl-simulator crate
2 parents 40364f9 + a5d7c84 commit bb53f52

File tree

6 files changed

+209
-0
lines changed

6 files changed

+209
-0
lines changed

Cargo.lock

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ members = [
99
"crates/metadata",
1010
"crates/parser",
1111
"crates/path",
12+
"crates/simulator",
1213
"crates/sourcemap",
1314
"crates/std",
1415
"crates/tests",

crates/simulator/Cargo.toml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[package]
2+
name = "veryl-simulator"
3+
version = "0.13.5"
4+
authors.workspace = true
5+
repository.workspace = true
6+
keywords.workspace = true
7+
categories.workspace = true
8+
license.workspace = true
9+
readme.workspace = true
10+
description.workspace = true
11+
edition.workspace = true
12+
13+
[dependencies]
14+
toml = {workspace = true}
15+
veryl-analyzer = {version = "0.13.5", path = "../analyzer"}
16+
veryl-metadata = {version = "0.13.5", path = "../metadata"}
17+
veryl-parser = {version = "0.13.5", path = "../parser"}
18+
veryl-path = {version = "0.13.5", path = "../path"}

crates/simulator/MEMO.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
シミュレータがやるべきことは
2+
3+
```systemverilog
4+
module Top (
5+
a: input logic<32>,
6+
b: input logic<32>,
7+
c: output logic<32>,
8+
){
9+
assign c = a + b;
10+
}
11+
```
12+
13+
のようなVerylのソースコードに対して
14+
15+
```rust
16+
let mut sim = Simulator::new("Top");
17+
sim.set("a", 10);
18+
sim.set("b", 20);
19+
sim.step();
20+
assert_eq!(sim.get("c"), 30);
21+
```
22+
23+
のようなRustのコードが通るようにすることです。
24+
サンプルとして、`./src/tests.rs` にこのテストを書いておきました。
25+
26+
27+
シミュレータが持つべき情報は
28+
29+
* 変数テーブル(a,b,cの現在の値を保持する)
30+
* 実行する文のテーブル(上の例ではassign文)
31+
32+
になると思います。
33+
シミュレータの実行が行うことは
34+
35+
* 変数テーブルの全変数に未評価のフラグをつける(newの時点)
36+
* setで変数に値をセットし未評価フラグをクリア
37+
* 変数テーブルから未評価の変数を取ってくる(ここではcだけが未評価)
38+
* 取ってきた変数に対応する文(ここではassign)を評価する
39+
* 文の評価中に未評価の変数が現れたら、それを深さ優先で再帰的に評価する
40+
* 未評価の変数がなくなったら終了
41+
42+
のようになります。RTLではソースコードの上から下に実行されるのではなく、各文は並列に実行される(つまりコードの下の方に書かれた文の結果が上の方にも影響する)モデルなので、このような実行方法になります。
43+
44+
ソースコードから変数テーブルと文のテーブルを構築するにはシンボルテーブルを参照します。
45+
シンボルテーブルは名前に対応するシンボル情報を持っています。
46+
Symbol構造体の定義は以下になります。
47+
48+
https://docs.rs/veryl-analyzer/latest/veryl_analyzer/symbol/struct.Symbol.html
49+
50+
シンボルの種類はSymbolKindにあり、
51+
52+
https://docs.rs/veryl-analyzer/latest/veryl_analyzer/symbol/enum.SymbolKind.html
53+
54+
各バリアント内の構造体に、その種類固有の情報が入っています。
55+
例えばModulePropertyにはdefinitionがあり(definitionを追加したバージョンはまだリリースしていないのでdocs.rsにはないです)、ここからモジュール定義の構文木全体を取得できるので、そこからassign文を抜き出せます。
56+
ここは将来的にはコンパイラの解析フェーズでシミュレータが必要な情報を事前に抜いてくるのもいいかもしれません。
57+
変数はSymbolKindがPort/Variableのものです。それがTopモジュール内にあるかどうかはnamespaceで確認できます。
58+
59+
Verylコンパイラ内では文字列などヒープを要するリソースはコピーや借用などが面倒なので全てID(usize)で管理していて、
60+
実体はスレッドローカルストレージ内のHashMapにあります。なので実体を参照したいときはテーブルから引いてくる必要があります。
61+
具体的なコードは `./src/tests.rs` にいくつか書いておきました。
62+
63+
また、式の評価はEvaluatorの実装が参考になると思います。将来的にはコンパイラ用とシミュレータ用は統合した方が良いかもしれませんが、まだ要件がはっきりしないので、とりあえずはシミュレータ専用に作るのが楽だと思います。
64+
65+
https://docs.rs/veryl-analyzer/latest/veryl_analyzer/evaluator/struct.Evaluator.html
66+
67+
進め方についてはとりあえず最初の例のような簡単なものから始めて
68+
69+
* always_comb文
70+
* クロックの導入(always_ff文)
71+
* モジュール階層
72+
* 関数呼出し
73+
74+
という感じで徐々に機能を増やしていくのがいいと思います。
75+
76+
あとRTLで扱う変数はとても大きくなることがよくある(256bitとか1024bitとか)ので、最初はusizeでもいいですが早いうちに何らかのbig integerクレートに移行するのがいいと思います。
77+
78+
構文定義は以下になります。この定義からRustのコードが生成されるので、書かれているノード名とRustの構造体名は同じになります。
79+
取ってきたい構文要素がどのように入っているかを追うにはこちらが便利かもしれません。
80+
81+
https://github.com/veryl-lang/veryl/blob/master/crates/parser/veryl.par

crates/simulator/src/lib.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
pub struct Simulator;
2+
3+
impl Simulator {
4+
pub fn new(_top: &str) -> Self {
5+
Self
6+
}
7+
8+
pub fn set(&mut self, _port: &str, _value: usize) {}
9+
10+
pub fn get(&mut self, _port: &str) -> usize {
11+
0
12+
}
13+
14+
pub fn step(&mut self) {}
15+
}
16+
17+
#[cfg(test)]
18+
mod tests;

crates/simulator/src/tests.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
use crate::Simulator;
2+
use veryl_analyzer::namespace::Namespace;
3+
use veryl_analyzer::symbol::SymbolKind;
4+
use veryl_analyzer::symbol_path::SymbolPath;
5+
use veryl_analyzer::{Analyzer, AnalyzerError, definition_table, symbol_table};
6+
use veryl_metadata::Metadata;
7+
use veryl_parser::{Parser, resource_table};
8+
9+
#[track_caller]
10+
fn analyze(code: &str) -> Vec<AnalyzerError> {
11+
symbol_table::clear();
12+
13+
let metadata: Metadata =
14+
toml::from_str(&Metadata::create_default_toml("prj").unwrap()).unwrap();
15+
let parser = Parser::parse(&code, &"").unwrap();
16+
let analyzer = Analyzer::new(&metadata);
17+
18+
let mut errors = vec![];
19+
errors.append(&mut analyzer.analyze_pass1(&"prj", &"", &parser.veryl));
20+
errors.append(&mut Analyzer::analyze_post_pass1());
21+
errors.append(&mut analyzer.analyze_pass2(&"prj", &"", &parser.veryl));
22+
errors.append(&mut analyzer.analyze_pass3(&"prj", &"", &parser.veryl));
23+
dbg!(&errors);
24+
errors
25+
}
26+
27+
#[test]
28+
fn simple_sim() {
29+
let code = r#"
30+
module Top (
31+
a: input logic<32>,
32+
b: input logic<32>,
33+
c: output logic<32>,
34+
) {
35+
assign c = a + b;
36+
}
37+
"#;
38+
39+
// Call Veryl compiler
40+
let errors = analyze(code);
41+
assert!(errors.is_empty());
42+
43+
let top = resource_table::insert_str("Top");
44+
let namespace = Namespace::default();
45+
let path = SymbolPath::new(&[top]);
46+
47+
// get top module definition
48+
if let Ok(symbol) = symbol_table::resolve((&path, &namespace)) {
49+
if let SymbolKind::Module(x) = &symbol.found.kind {
50+
let top_define = definition_table::get(x.definition);
51+
dbg!(top_define);
52+
}
53+
}
54+
55+
// get port/variable symbol information
56+
for symbol in symbol_table::get_all() {
57+
match symbol.kind {
58+
SymbolKind::Port(x) => {
59+
dbg!(x);
60+
}
61+
SymbolKind::Variable(x) => {
62+
dbg!(x);
63+
}
64+
_ => (),
65+
}
66+
}
67+
68+
// Create new simulator instance specifing "Top" as top module
69+
let mut sim = Simulator::new("Top");
70+
71+
// Set values to input ports
72+
sim.set("a", 10);
73+
sim.set("b", 20);
74+
75+
// Execute 1 clock cycle simulation
76+
sim.step();
77+
78+
// Get values from output ports
79+
//assert_eq!(sim.get("c"), 30);
80+
}

0 commit comments

Comments
 (0)