-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Jacob Urbanczyk
committed
Feb 29, 2024
1 parent
5a7390c
commit e475867
Showing
5 changed files
with
150 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import operator | ||
from functools import reduce | ||
from dataclasses import dataclass, field | ||
from dataclasses_json import dataclass_json | ||
from typing import TypeAlias | ||
|
||
from amaranth import * | ||
from amaranth.tracer import get_src_loc | ||
|
||
from transactron.utils import SrcLoc | ||
from transactron.utils._typing import ModuleLike, ValueLike | ||
from transactron.utils.dependencies import DependencyContext, ListKey | ||
|
||
LogLevel: TypeAlias = int | ||
|
||
DEBUG: LogLevel = 10 | ||
INFO: LogLevel = 20 | ||
WARNING: LogLevel = 30 | ||
ERROR: LogLevel = 40 | ||
|
||
_level_to_name = { | ||
ERROR: "ERROR", | ||
WARNING: "WARN", | ||
INFO: "INFO", | ||
DEBUG: "DEBUG", | ||
} | ||
|
||
|
||
def get_level_name(level: LogLevel) -> str: | ||
if level in _level_to_name: | ||
return _level_to_name[level] | ||
return "Level {}".format(level) | ||
|
||
|
||
@dataclass_json | ||
@dataclass(frozen=True) | ||
class LogEntryDescription: | ||
level: LogLevel | ||
format_str: str | ||
location: SrcLoc | ||
|
||
def format(self, *args) -> str: | ||
return self.format_str.format(*args) | ||
|
||
|
||
@dataclass(frozen=True) | ||
class LogEntry(LogEntryDescription): | ||
trigger: Signal | ||
fields: list[Signal] = field(default_factory=list) | ||
|
||
|
||
@dataclass(frozen=True) | ||
class LogKey(ListKey[LogEntry]): | ||
pass | ||
|
||
|
||
def log(m: ModuleLike, level: LogLevel, format: str, trigger: ValueLike, *args, src_loc_at: int = 0): | ||
""" | ||
Parameters | ||
---------- | ||
src_loc_at : int, optional | ||
How many stack frames below to look for the source location, used to | ||
identify the failing assertion. | ||
""" | ||
|
||
# TODO: make the location relative to the root of the project | ||
src_loc = get_src_loc(src_loc_at + 1) | ||
|
||
trigger_signal = Signal() | ||
m.d.comb += trigger_signal.eq(trigger) | ||
|
||
entry = LogEntry(level=level, format_str=format, location=src_loc, trigger=trigger_signal) | ||
|
||
for arg in args: | ||
sig = Signal.like(arg) | ||
m.d.top_comb += sig.eq(arg) | ||
entry.fields.append(sig) | ||
|
||
dependencies = DependencyContext.get() | ||
dependencies.add_dependency(LogKey(), entry) | ||
|
||
|
||
def debug(m: ModuleLike, trigger: ValueLike, format: str, *args, **kwargs): | ||
log(m, DEBUG, format, trigger, *args, **kwargs) | ||
|
||
|
||
def get_logs(log_level: LogLevel) -> list[LogEntry]: | ||
dependencies = DependencyContext.get() | ||
all_logs = dependencies.get_dependency(LogKey()) | ||
return [entry for entry in all_logs if entry.level >= log_level] | ||
|
||
|
||
def get_trigger_bit(log_level: LogLevel) -> Value: | ||
return reduce(operator.and_, [entry.trigger for entry in get_logs(log_level)], C(1)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
from amaranth.sim import Passive, Tick | ||
from transactron.lib import log | ||
|
||
|
||
__all__ = ["make_log_process"] | ||
|
||
|
||
def make_log_process(log_level: log.LogLevel): | ||
combined_trigger = log.get_trigger_bit(log_level) | ||
cycle = 0 | ||
|
||
def handle_logs(): | ||
if not (yield combined_trigger): | ||
return | ||
|
||
for entry in log.get_logs(log_level): | ||
if not (yield entry.trigger): | ||
return | ||
|
||
values: list[int] = [] | ||
for field in entry.fields: | ||
values.append((yield field)) | ||
formatted_msg = entry.format(*values) | ||
print( | ||
"{} {} {}:{}] {}".format( | ||
log.get_level_name(entry.level), cycle, entry.location[0], entry.location[1], formatted_msg | ||
) | ||
) | ||
|
||
def log_process(): | ||
nonlocal cycle | ||
|
||
yield Passive() | ||
while True: | ||
yield Tick("sync_neg") | ||
yield from handle_logs() | ||
yield | ||
cycle += 1 | ||
|
||
return log_process |