Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v0.5.0

## 📑 Table of Contents

- [Version 0.13.39 (2025-10-19)](#version-01339-2025-10-19)
- [Version 0.13.38 (2025-10-19)](#version-01338-2025-10-19)
- [Version 0.13.37 (2025-09-30)](#version-01337-2025-09-30)
- [Version 0.13.36 (2025-09-29)](#version-01336-2025-09-29)
- [Version 0.13.35 (2025-09-26)](#version-01335-2025-09-26)
Expand Down Expand Up @@ -49,6 +51,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v0.5.0
- [Version 0.13.4 (2025-08-16)](#version-0134-2025-08-16)
- [Version 0.5.8 (2025-06-09)](#020---2025-06-09)

---
## Version 0.13.39 (2025-10-19)

Minor updates and improvements.

---
## Version 0.13.38 (2025-10-19)

Minor updates and improvements.

---
## Version 0.13.37 (2025-09-30)

Expand Down
8 changes: 4 additions & 4 deletions src/__version__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/usr/bin/env python3
"""Version information for PCILeech Firmware Generator."""

__version__ = "0.13.37"
__version_info__ = (0, 13, 37)
__version__ = "0.13.39"
__version_info__ = (0, 13, 39)

# Release information
__title__ = "PCILeech Firmware Generator"
Expand All @@ -13,5 +13,5 @@
__url__ = "https://github.com/voltcyclone/PCILeechFWGenerator"

# Build metadata
__build_date__ = "2025-10-01T03:02:54.782549"
__commit_hash__ = "2cefaae"
__build_date__ = "2025-10-19T06:30:42.327374"
__commit_hash__ = "d023f3d"
27 changes: 27 additions & 0 deletions src/behavioral/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/usr/bin/env python3
"""Behavioral device simulation for PCILeech firmware generation.

This module provides behavioral simulation capabilities that make generated
firmware behave like real devices (e.g., Ethernet showing as connected,
NVMe showing as ready).
"""

from .base import (
BehaviorType,
BehavioralRegister,
BehavioralCounter,
BehavioralSpec
)
from .analyzer import (
BehavioralAnalyzerFactory,
generate_behavioral_spec
)

__all__ = [
'BehaviorType',
'BehavioralRegister',
'BehavioralCounter',
'BehavioralSpec',
'BehavioralAnalyzerFactory',
'generate_behavioral_spec',
]
56 changes: 56 additions & 0 deletions src/behavioral/analyzer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""Behavioral analyzer factory and dispatcher."""

import logging
from typing import Any, Optional

from string_utils import log_info_safe, log_warning_safe, safe_format
Copy link

Copilot AI Oct 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import path 'string_utils' is incorrect. Based on the codebase structure, this should be 'from src.string_utils import ...' to maintain consistency.

Suggested change
from string_utils import log_info_safe, log_warning_safe, safe_format
from src.string_utils import log_info_safe, log_warning_safe, safe_format

Copilot uses AI. Check for mistakes.

from .base import BehavioralSpec
from .network_behavioral import NetworkBehavioralAnalyzer
from .storage_behavioral import StorageBehavioralAnalyzer
from .media_behavioral import MediaBehavioralAnalyzer


logger = logging.getLogger(__name__)


class BehavioralAnalyzerFactory:
"""Factory for creating device-specific behavioral analyzers."""

@staticmethod
def create_analyzer(device_config: Any) -> Optional[Any]:
"""Create appropriate behavioral analyzer based on device class."""
class_code = getattr(device_config, 'class_code', 0)
device_class = (class_code >> 16) & 0xFF

log_info_safe(logger, safe_format("Creating behavioral analyzer for class=0x{cls:02X}",
cls=device_class))

if device_class == 0x02: # Network controller
return NetworkBehavioralAnalyzer(device_config)
elif device_class == 0x01: # Storage controller
return StorageBehavioralAnalyzer(device_config)
elif device_class == 0x04: # Multimedia controller
return MediaBehavioralAnalyzer(device_config)
else:
log_warning_safe(logger, safe_format("No behavioral analyzer for class=0x{cls:02X}",
cls=device_class))
return None

@staticmethod
def generate_behavioral_spec(device_config: Any) -> Optional[BehavioralSpec]:
"""Generate behavioral specification for device."""
if not getattr(device_config, 'enable_behavioral_simulation', False):
log_info_safe(logger, "Behavioral simulation disabled")
return None

analyzer = BehavioralAnalyzerFactory.create_analyzer(device_config)
if not analyzer:
return None

return analyzer.generate_spec()


def generate_behavioral_spec(device_config: Any) -> Optional[BehavioralSpec]:
"""Convenience function to generate behavioral spec."""
return BehavioralAnalyzerFactory.generate_behavioral_spec(device_config)
131 changes: 131 additions & 0 deletions src/behavioral/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#!/usr/bin/env python3
"""Base behavioral register infrastructure for device simulation."""

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

Check notice

Code scanning / CodeQL

Unused import Note

Import of 'List' is not used.

Copilot Autofix

AI 5 days ago

To fix the problem, remove the unused import of List from the statement from typing import Dict, Any, List, Optional on line 5 in src/behavioral/base.py. The best way is to edit the import so it only includes the names that are still needed (Dict, Any, and Optional). No other changes are required, and this does not affect any functionality of the code.

Suggested changeset 1
src/behavioral/base.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/behavioral/base.py b/src/behavioral/base.py
--- a/src/behavioral/base.py
+++ b/src/behavioral/base.py
@@ -2,7 +2,7 @@
 """Base behavioral register infrastructure for device simulation."""
 
 import logging
-from typing import Dict, Any, List, Optional
+from typing import Dict, Any, Optional
 from dataclasses import dataclass
 from enum import Enum
 
EOF
@@ -2,7 +2,7 @@
"""Base behavioral register infrastructure for device simulation."""

import logging
from typing import Dict, Any, List, Optional
from typing import Dict, Any, Optional
from dataclasses import dataclass
from enum import Enum

Copilot is powered by AI and may make mistakes. Always verify output.
from dataclasses import dataclass
from enum import Enum

from string_utils import log_error_safe, log_debug_safe, safe_format
Copy link

Copilot AI Oct 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import path 'string_utils' is incorrect. Based on the codebase structure, this should be 'from src.string_utils import ...' to maintain consistency.

Suggested change
from string_utils import log_error_safe, log_debug_safe, safe_format
from src.string_utils import log_error_safe, log_debug_safe, safe_format

Copilot uses AI. Check for mistakes.



logger = logging.getLogger(__name__)


class BehaviorType(Enum):
"""Register behavior types for simulation."""
CONSTANT = "constant" # Always returns fixed value
AUTO_INCREMENT = "auto_increment" # Auto-incrementing counter
WRITE_CAPTURE = "write_capture" # Captures written value
RANDOM = "random" # Random data generation
PATTERN = "pattern" # Pattern-based generation
TRIGGERED = "triggered" # State change on trigger
PERIODIC = "periodic" # Periodic value changes


@dataclass
class BehavioralRegister:
"""Definition of a behavioral register."""
name: str
offset: int
behavior: BehaviorType
default_value: int = 0x00000000
pattern: Optional[str] = None
counter_bits: Optional[int] = None
description: str = ""
read_only: bool = False

def to_dict(self) -> Dict[str, Any]:
"""Convert to template-compatible dictionary."""
result = {
"offset": self.offset,
"behavior": self.behavior.value,
"default": self.default_value,
"description": self.description,
"read_only": self.read_only
}
if self.pattern:
result["pattern"] = self.pattern
if self.counter_bits:
result["counter_bits"] = self.counter_bits
return result


@dataclass
class BehavioralCounter:
"""Definition of a behavioral counter."""
name: str
width: int
increment_rate: int = 1
reset_value: int = 0
description: str = ""

def to_dict(self) -> Dict[str, Any]:
"""Convert to template-compatible dictionary."""
return {
"width": self.width,
"increment_rate": self.increment_rate,
"reset_value": self.reset_value,
"description": self.description
}


class BehavioralSpec:
"""Complete behavioral specification for a device."""

def __init__(self, device_category: str):
self.device_category = device_category
self.registers: Dict[str, BehavioralRegister] = {}
self.counters: Dict[str, BehavioralCounter] = {}
self.state_machines: Dict[str, Any] = {}

def add_register(self, register: BehavioralRegister) -> None:
"""Add a behavioral register."""
log_debug_safe(logger, safe_format("Adding behavioral register: {name} at 0x{offset:04X}",
name=register.name, offset=register.offset))
self.registers[register.name] = register

def add_counter(self, counter: BehavioralCounter) -> None:
"""Add a behavioral counter."""
log_debug_safe(logger, safe_format("Adding counter: {name} ({width} bits)",
name=counter.name, width=counter.width))
self.counters[counter.name] = counter

def to_dict(self) -> Dict[str, Any]:
"""Convert to template-compatible dictionary."""
return {
"device_category": self.device_category,
"registers": {k: v.to_dict() for k, v in self.registers.items()},
"counters": {k: v.to_dict() for k, v in self.counters.items()},
"state_machines": self.state_machines
}

def validate(self) -> bool:
"""Validate the behavioral specification."""
# Check for offset conflicts
offsets = {}
for name, reg in self.registers.items():
if reg.offset in offsets:
log_error_safe(logger, safe_format("Offset conflict: {name1} and {name2} at 0x{offset:04X}",
name1=offsets[reg.offset], name2=name, offset=reg.offset))
return False
offsets[reg.offset] = name

# Validate counter references in patterns
for name, reg in self.registers.items():
if reg.pattern and reg.behavior == BehaviorType.AUTO_INCREMENT:
# Check if pattern references valid counters
for counter_name in self.counters.keys():
if counter_name in reg.pattern:
log_debug_safe(logger, safe_format("Register {reg} uses counter {cnt}",
reg=name, cnt=counter_name))

return True


def require(condition: bool, message: str, **context) -> None:
"""Validate condition or exit with error."""
if not condition:
log_error_safe(safe_format("Build aborted: {msg} | ctx={ctx}",
Copy link

Copilot AI Oct 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The log_error_safe function call is missing the logger parameter as the first argument. This should be 'log_error_safe(logger, safe_format(...))' to match the function signature used elsewhere in the codebase.

Suggested change
log_error_safe(safe_format("Build aborted: {msg} | ctx={ctx}",
log_error_safe(logger, safe_format("Build aborted: {msg} | ctx={ctx}",

Copilot uses AI. Check for mistakes.

msg=message, ctx=context))
raise SystemExit(2)
Loading
Loading