File size: 2,863 Bytes
4344b33
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
from __future__ import annotations

import re
from typing import Any, Dict, List, Optional


PROTOCOL_SIGNATURES = {
    "uart": {"tx", "rx", "baud"},
    "spi": {"mosi", "miso", "sclk", "ss_n", "cs"},
    "i2c": {"scl", "sda"},
    "axi4lite": {"awvalid", "awready", "arvalid", "arready",
                  "wvalid", "wready", "rvalid", "rready",
                  "bvalid", "bready"},
    "apb": {"psel", "penable", "paddr", "pwrite", "pready"},
    "wishbone": {"wb_cyc", "wb_stb", "wb_we", "wb_ack", "wb_addr"},
}


class SpecPreprocessor:
    def preprocess(self, raw: Dict[str, Any]) -> Dict[str, Any]:
        raw = self._normalise_names(raw)
        raw = self._default_clock_reset(raw)
        raw = self._expand_signals(raw)
        raw = self._validate_address_formats(raw)
        raw = self._detect_protocol(raw)
        return raw

    @staticmethod
    def _normalise_names(raw: Dict[str, Any]) -> Dict[str, Any]:
        if "design_name" in raw:
            raw["design_name"] = re.sub(r"[^a-zA-Z0-9_]", "_", raw["design_name"]).lower()
        return raw

    @staticmethod
    def _default_clock_reset(raw: Dict[str, Any]) -> Dict[str, Any]:
        raw.setdefault("clock_reset", {"clock": "clk", "reset": "rst_n", "reset_active": 0})
        return raw

    @staticmethod
    def _expand_signals(raw: Dict[str, Any]) -> Dict[str, Any]:
        for iface in raw.get("interfaces", []):
            for sig in iface.get("signals", []):
                sig.setdefault("width", 1)
        return raw

    @staticmethod
    def _validate_address_formats(raw: Dict[str, Any]) -> Dict[str, Any]:
        for reg in raw.get("registers", []):
            addr = reg.get("address", "0x00")
            if not isinstance(addr, str) or not addr.startswith("0x"):
                reg["address"] = f"0x{int(addr):02X}" if isinstance(addr, int) else f"0x{addr}"
            for field in reg.get("fields", []):
                bits = field.get("bits")
                if bits is not None and not isinstance(bits, str):
                    field["bits"] = str(bits)
        return raw

    @staticmethod
    def _detect_protocol(raw: Dict[str, Any]) -> Dict[str, Any]:
        signal_names = set()
        for iface in raw.get("interfaces", []):
            for sig in iface.get("signals", []):
                signal_names.add(sig.get("name", "").lower())

        if raw.get("protocol"):
            return raw

        detected = None
        rank = 0
        for proto, sigs in PROTOCOL_SIGNATURES.items():
            matches = sum(1 for kw in sigs if any(kw in s for s in signal_names))
            if matches > rank:
                rank = matches
                detected = proto

        if detected and rank >= 2:
            raw["protocol"] = detected
        elif not raw.get("protocol"):
            raw["protocol"] = "wishbone"

        return raw