python-library-ralf-model 0.1.1__tar.gz → 0.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (18) hide show
  1. {python_library_ralf_model-0.1.1 → python_library_ralf_model-0.1.2}/PKG-INFO +3 -1
  2. {python_library_ralf_model-0.1.1 → python_library_ralf_model-0.1.2}/README.md +2 -0
  3. {python_library_ralf_model-0.1.1 → python_library_ralf_model-0.1.2}/pyproject.toml +1 -1
  4. {python_library_ralf_model-0.1.1 → python_library_ralf_model-0.1.2}/ralf_model/emit.py +4 -0
  5. {python_library_ralf_model-0.1.1 → python_library_ralf_model-0.1.2}/ralf_model/nodes.py +8 -0
  6. {python_library_ralf_model-0.1.1 → python_library_ralf_model-0.1.2}/ralf_model/parse.py +13 -1
  7. {python_library_ralf_model-0.1.1 → python_library_ralf_model-0.1.2}/tests/test_ralf_model.py +63 -0
  8. {python_library_ralf_model-0.1.1 → python_library_ralf_model-0.1.2}/.gitignore +0 -0
  9. {python_library_ralf_model-0.1.1 → python_library_ralf_model-0.1.2}/example/__main__.py +0 -0
  10. {python_library_ralf_model-0.1.1 → python_library_ralf_model-0.1.2}/example.bat +0 -0
  11. {python_library_ralf_model-0.1.1 → python_library_ralf_model-0.1.2}/ralf_model/__init__.py +0 -0
  12. {python_library_ralf_model-0.1.1 → python_library_ralf_model-0.1.2}/ralf_model/abc.py +0 -0
  13. {python_library_ralf_model-0.1.1 → python_library_ralf_model-0.1.2}/ralf_model/errors.py +0 -0
  14. {python_library_ralf_model-0.1.1 → python_library_ralf_model-0.1.2}/ralf_model/io.py +0 -0
  15. {python_library_ralf_model-0.1.1 → python_library_ralf_model-0.1.2}/ralf_model/source_expand.py +0 -0
  16. {python_library_ralf_model-0.1.1 → python_library_ralf_model-0.1.2}/test.bat +0 -0
  17. {python_library_ralf_model-0.1.1 → python_library_ralf_model-0.1.2}/tests/__init__.py +0 -0
  18. {python_library_ralf_model-0.1.1 → python_library_ralf_model-0.1.2}/tests/test_source_include.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-library-ralf-model
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Requires-Python: >=3.10
5
5
  Requires-Dist: pydantic<3,>=2.0
6
6
  Description-Content-Type: text/markdown
@@ -41,6 +41,8 @@ doc = load_ralf_file(
41
41
  ## 能力范围
42
42
 
43
43
  - **block**:定义 ``block 名 { ... }``;简单映射 ``block 名 @地址;``;赋值与可选路径、地址 ``block 左名 = 右名``、``block 左名 = 右名 (hdl路径)``、``... @地址``,可与 ``{ ... }`` 组合。
44
+ - **register**:``register 名 [ (hdl路径) ] [ @字节偏移 ] { ... }`` 或 ``register 名;`` 前向引用。
45
+ - **field**:``field 名 [(hdl路径)] [ @位偏移 ] { ... }``(括号路径可紧贴字段名,如 ``field f(hdl.f)``)。
44
46
  - `field` 花括号内按**源顺序**保留各条语句(含 `enum { ... };` 等),便于往返。
45
47
  - `@` 后的偏移在写出时统一为 Verilog 风格十六进制字面量(如 `'h5`);`bytes` 等为十进制。
46
48
 
@@ -34,6 +34,8 @@ doc = load_ralf_file(
34
34
  ## 能力范围
35
35
 
36
36
  - **block**:定义 ``block 名 { ... }``;简单映射 ``block 名 @地址;``;赋值与可选路径、地址 ``block 左名 = 右名``、``block 左名 = 右名 (hdl路径)``、``... @地址``,可与 ``{ ... }`` 组合。
37
+ - **register**:``register 名 [ (hdl路径) ] [ @字节偏移 ] { ... }`` 或 ``register 名;`` 前向引用。
38
+ - **field**:``field 名 [(hdl路径)] [ @位偏移 ] { ... }``(括号路径可紧贴字段名,如 ``field f(hdl.f)``)。
37
39
  - `field` 花括号内按**源顺序**保留各条语句(含 `enum { ... };` 等),便于往返。
38
40
  - `@` 后的偏移在写出时统一为 Verilog 风格十六进制字面量(如 `'h5`);`bytes` 等为十进制。
39
41
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "python-library-ralf-model"
7
- version = "0.1.1"
7
+ version = "0.1.2"
8
8
  readme = "README.md"
9
9
  requires-python = ">=3.10"
10
10
  dependencies = [
@@ -12,6 +12,8 @@ def _fmt_at_int(v: int) -> str:
12
12
 
13
13
  def _emit_field(f: FieldNode, indent: str) -> list[str]:
14
14
  head = f"{indent}field {f.name}"
15
+ if f.paren_path is not None:
16
+ head += f"({f.paren_path})"
15
17
  if f.offset_bits is not None:
16
18
  head += f" @{_fmt_at_int(f.offset_bits)}"
17
19
  head += " {"
@@ -26,6 +28,8 @@ def _emit_field(f: FieldNode, indent: str) -> list[str]:
26
28
 
27
29
  def _emit_register(r: RegisterNode, indent: str) -> list[str]:
28
30
  head = f"{indent}register {r.name}"
31
+ if r.paren_path is not None:
32
+ head += f" ({r.paren_path})"
29
33
  if r.offset_bytes is not None:
30
34
  head += f" @{_fmt_at_int(r.offset_bytes)}"
31
35
  if r.declaration_only:
@@ -9,6 +9,10 @@ class FieldNode(BaseModel):
9
9
  model_config = ConfigDict(extra="forbid")
10
10
 
11
11
  name: str
12
+ paren_path: str | None = Field(
13
+ default=None,
14
+ description="紧跟在 field 名后的圆括号路径内容(不含括号),如 ``field f(hdl.sig)``",
15
+ )
12
16
  offset_bits: int | None = Field(default=None, description="field 内 `@` 后的位偏移")
13
17
  inner_statements: list[str] = Field(
14
18
  default_factory=list,
@@ -22,6 +26,10 @@ class RegisterNode(BaseModel):
22
26
  model_config = ConfigDict(extra="forbid")
23
27
 
24
28
  name: str
29
+ paren_path: str | None = Field(
30
+ default=None,
31
+ description="紧跟在 register 名后的圆括号路径内容(不含括号),如 ``register r (dut.reg)``",
32
+ )
25
33
  offset_bytes: int | None = Field(default=None, description="register 后的 `@` 字节偏移")
26
34
  bytes_width: int | None = None
27
35
  fields: list[FieldNode] = Field(default_factory=list)
@@ -349,6 +349,10 @@ class _Parser:
349
349
  self.expect_keyword("register")
350
350
  name = self.read_ident()
351
351
  self.skip_ws_and_comments()
352
+ paren_path: str | None = None
353
+ if self._peek() == "(":
354
+ paren_path = self.read_round_paren_inner()
355
+ self.skip_ws_and_comments()
352
356
  offset: int | None = None
353
357
  if self._peek() == "@":
354
358
  self._advance()
@@ -358,6 +362,7 @@ class _Parser:
358
362
  self._advance()
359
363
  return RegisterNode(
360
364
  name=name,
365
+ paren_path=paren_path,
361
366
  offset_bytes=offset,
362
367
  declaration_only=True,
363
368
  )
@@ -367,6 +372,7 @@ class _Parser:
367
372
  self.expect_char("}")
368
373
  return RegisterNode(
369
374
  name=name,
375
+ paren_path=paren_path,
370
376
  offset_bytes=offset,
371
377
  bytes_width=rbw,
372
378
  fields=fields,
@@ -396,6 +402,10 @@ class _Parser:
396
402
  self.expect_keyword("field")
397
403
  name = self.read_ident()
398
404
  self.skip_ws_and_comments()
405
+ paren_path: str | None = None
406
+ if self._peek() == "(":
407
+ paren_path = self.read_round_paren_inner()
408
+ self.skip_ws_and_comments()
399
409
  off_bits: int | None = None
400
410
  if self._peek() == "@":
401
411
  self._advance()
@@ -403,7 +413,9 @@ class _Parser:
403
413
  self.expect_char("{")
404
414
  fn = self._parse_field_body()
405
415
  self.expect_char("}")
406
- return fn.model_copy(update={"name": name, "offset_bits": off_bits})
416
+ return fn.model_copy(
417
+ update={"name": name, "paren_path": paren_path, "offset_bits": off_bits}
418
+ )
407
419
 
408
420
  def _parse_field_body(self) -> FieldNode:
409
421
  inner: list[str] = []
@@ -148,6 +148,69 @@ block top {
148
148
  out = dump_ralf(doc)
149
149
  self.assertIn("register R0;", out)
150
150
 
151
+ def test_register_paren_path_at_offset(self) -> None:
152
+ src = """
153
+ block top {
154
+ bytes 4;
155
+ register CTRL (dut.ctrl_reg) @'h0 {
156
+ bytes 4;
157
+ field ena(dut.ctrl_reg.en) @1 {
158
+ bits 1;
159
+ access rw;
160
+ }
161
+ }
162
+ }
163
+ """
164
+ doc = parse_ralf(src)
165
+ reg = doc.blocks[0].registers[0]
166
+ self.assertEqual(reg.name, "CTRL")
167
+ self.assertEqual(reg.paren_path, "dut.ctrl_reg")
168
+ self.assertEqual(reg.offset_bytes, 0)
169
+ fld = reg.fields[0]
170
+ self.assertEqual(fld.name, "ena")
171
+ self.assertEqual(fld.paren_path, "dut.ctrl_reg.en")
172
+ self.assertEqual(fld.offset_bits, 1)
173
+ out = dump_ralf(doc)
174
+ self.assertIn("register CTRL (dut.ctrl_reg) @'h0", out)
175
+ self.assertIn("field ena(dut.ctrl_reg.en) @'h1", out)
176
+ doc2 = parse_ralf(out)
177
+ self.assertEqual(doc.model_dump(), doc2.model_dump())
178
+
179
+ def test_register_paren_path_forward_decl(self) -> None:
180
+ src = """
181
+ block top {
182
+ register R0 (hdl.r0) @'h10;
183
+ }
184
+ """
185
+ doc = parse_ralf(src)
186
+ r = doc.blocks[0].registers[0]
187
+ self.assertTrue(r.declaration_only)
188
+ self.assertEqual(r.paren_path, "hdl.r0")
189
+ self.assertEqual(r.offset_bytes, 0x10)
190
+ out = dump_ralf(doc)
191
+ self.assertIn("register R0 (hdl.r0) @'h10;", out)
192
+ doc2 = parse_ralf(out)
193
+ self.assertEqual(doc.model_dump(), doc2.model_dump())
194
+
195
+ def test_field_paren_path_no_space(self) -> None:
196
+ src = """
197
+ block top {
198
+ register r {
199
+ bytes 4;
200
+ field sig(hdl.sig) @0 {
201
+ bits 4;
202
+ }
203
+ }
204
+ }
205
+ """
206
+ doc = parse_ralf(src)
207
+ fld = doc.blocks[0].registers[0].fields[0]
208
+ self.assertEqual(fld.paren_path, "hdl.sig")
209
+ out = dump_ralf(doc)
210
+ self.assertIn("field sig(hdl.sig)", out)
211
+ doc2 = parse_ralf(out)
212
+ self.assertEqual(doc.model_dump(), doc2.model_dump())
213
+
151
214
  def test_block_ref_rhs_with_parentheses(self) -> None:
152
215
  src = """
153
216
  block top {