OpenSUSI-MPW
Open-Source TR-1um を使った OpenSUSI-MPW サービスを使って「何か本格的に設計したいなぁ」と考えていたのですが、折角なら Open-Source Tool を活用するリファレンス設計としてのドキュメント記録も残したいと思い始めました。そこで、1999 年にT社からザ○○に転職した頃、トップダウン設計の重要性をエンジニアメンバーと会社幹部にデモする目的でスクラッチから独自開発した 「Octave での PLL 設計手法」 を現代に焼き直して 「Python での PLL/DLL 設計手法」 として公開することにします。
第11回は 「Simulation History #10」 を進めます。
#1 Project Foundation ✓
#2 Simulation Architecture ✓
#3 Parameter Definition ✓
#4 Simulation State ✓
#5 Event Simulation Engine ✓
#6 Ideal Delay Model ✓
#7 Ideal Phase Detector ✓
#8 Ideal Loop Controller ✓
#9 Lock Detector ✓
---
#10 Simulation History
#11 Reference Clock
#12 Visualization
ゴール
DLLSimulator 内に直接保持している履歴用 dict と記録処理を、独立した SimulationHistory クラスへ移します。
現在は DLLSimulator が、次の二つの責務を持っています。
- シミュレーションを進行する
- 各サイクルの結果を記録する
この記録責務を分離するので、dll/history.py と tests/test_history.py を作成します。
dll/history.py
"""
history.py
"""
from dll.state import SimulationState
class SimulationHistory:
def __init__(self):
self.clear()
@property
def data(self) -> dict[str, list]:
return {
"cycle": self.cycle,
"ref_edge_time": self.ref_edge_time,
"fb_edge_time": self.fb_edge_time,
"phase_error": self.phase_error,
"control": self.control,
"delay": self.delay,
"locked": self.locked,
}
def clear(self):
self.cycle = []
self.ref_edge_time = []
self.fb_edge_time = []
self.phase_error = []
self.control = []
self.delay = []
self.locked = []
def record(
self,
state: SimulationState,
):
#
# Store a snapshot of the current simulation state.
#
self.cycle.append(
state.cycle
)
self.ref_edge_time.append(
state.ref_edge_time
)
self.fb_edge_time.append(
state.fb_edge_time
)
self.phase_error.append(
state.phase_error
)
self.control.append(
state.control
)
self.delay.append(
state.delay
)
self.locked.append(
state.locked
)
simulator.py の変更
from dll.history import SimulationHistory
の追加。
self.history = SimulationHistory()
init() の履歴辞書を置き換える。さらに reset() の変更
self.history.clear()
履歴への個別追加をすべて削除して、
self.history.record(state)
に置き換えます。run() の戻り値は、
return self.history.data
に変更します。
tests/test_history.py
| Test | Specification |
|---|---|
test_create_history() |
Historyを生成できる |
test_initial_history_is_empty() |
初期状態ではすべての履歴が空である |
test_record_state() |
SimulationStateを1サイクル分記録できる |
test_record_multiple_states() |
複数サイクルを記録順に保存できる |
test_record_creates_snapshot() |
記録後にSimulationStateを変更しても履歴は変化しない |
test_clear_history() |
すべての履歴を消去できる |
test_history_fields_have_same_length() |
すべての履歴項目の要素数は常に一致する |
test_repeatability() |
同じSimulationState列を記録すると同じ履歴になる |
test_state_is_not_modified() |
History記録によってSimulationStateを変更しない |
"""
test_history.py
"""
from dll.history import SimulationHistory
from dll.params import DLLParams
from dll.state import SimulationState
#
# ============================================================
# Test Helper
# ============================================================
#
def create_state() -> SimulationState:
params = DLLParams.default()
state = SimulationState.initial(
params
)
return state
#
# ============================================================
# Constructor
# ============================================================
#
def test_create_history():
history = SimulationHistory()
assert isinstance(
history,
SimulationHistory,
)
#
# ============================================================
# Initial History
# ============================================================
#
def test_initial_history_is_empty():
history = SimulationHistory()
assert history.data == {
"cycle": [],
"ref_edge_time": [],
"fb_edge_time": [],
"phase_error": [],
"control": [],
"delay": [],
"locked": [],
}
#
# ============================================================
# Record One State
# ============================================================
#
def test_record_state():
history = SimulationHistory()
state = create_state()
state.cycle = 3
state.ref_edge_time = 30e-9
state.fb_edge_time = 32e-9
state.phase_error = -2e-9
state.control = -0.1
state.delay = 2e-9
state.locked = False
history.record(
state
)
assert history.data["cycle"] == [3]
assert history.data["ref_edge_time"] == [30e-9]
assert history.data["fb_edge_time"] == [32e-9]
assert history.data["phase_error"] == [-2e-9]
assert history.data["control"] == [-0.1]
assert history.data["delay"] == [2e-9]
assert history.data["locked"] == [False]
#
# ============================================================
# Record Multiple States
# ============================================================
#
def test_record_multiple_states():
history = SimulationHistory()
state = create_state()
for cycle in range(3):
state.cycle = cycle
state.ref_edge_time = cycle * 10e-9
state.fb_edge_time = cycle * 10e-9 + 2e-9
state.phase_error = -2e-9
state.control = -0.1 * cycle
state.delay = 2e-9
state.locked = (cycle == 2)
history.record(
state
)
assert history.data["cycle"] == [
0,
1,
2,
]
assert history.data["locked"] == [
False,
False,
True,
]
#
# ============================================================
# Snapshot
# ============================================================
#
def test_record_creates_snapshot():
history = SimulationHistory()
state = create_state()
state.cycle = 1
state.control = 0.25
state.delay = 5e-9
history.record(
state
)
state.cycle = 100
state.control = 999.0
state.delay = 999e-9
assert history.data["cycle"] == [1]
assert history.data["control"] == [0.25]
assert history.data["delay"] == [5e-9]
#
# ============================================================
# Clear History
# ============================================================
#
def test_clear_history():
history = SimulationHistory()
state = create_state()
history.record(
state
)
history.clear()
assert history.data == {
"cycle": [],
"ref_edge_time": [],
"fb_edge_time": [],
"phase_error": [],
"control": [],
"delay": [],
"locked": [],
}
#
# ============================================================
# History Length
# ============================================================
#
def test_history_fields_have_same_length():
history = SimulationHistory()
state = create_state()
for cycle in range(5):
state.cycle = cycle
history.record(
state
)
lengths = [
len(values)
for values in history.data.values()
]
assert len(
set(lengths)
) == 1
assert lengths[0] == 5
#
# ============================================================
# Repeatability
# ============================================================
#
def test_repeatability():
history1 = SimulationHistory()
history2 = SimulationHistory()
for history in (
history1,
history2,
):
state = create_state()
for cycle in range(3):
state.cycle = cycle
state.control = cycle
history.record(
state
)
assert history1.data == history2.data
#
# ============================================================
# State Integrity
# ============================================================
#
def test_state_is_not_modified():
history = SimulationHistory()
state = create_state()
original = (
state.cycle,
state.ref_edge_time,
state.fb_edge_time,
state.phase_error,
state.control,
state.delay,
state.locked,
state.lock_counter,
)
history.record(
state
)
current = (
state.cycle,
state.ref_edge_time,
state.fb_edge_time,
state.phase_error,
state.control,
state.delay,
state.locked,
state.lock_counter,
)
assert current == original
pytest の実行
python -m pytest
======================================================= test session starts =======================================================
platform darwin -- Python 3.14.6, pytest-9.1.1, pluggy-1.6.0
rootdir: /Users/okamura/Dropbox/98_LSI_Design/DLL_TopDown
collected 65 items
tests/test_controller.py ......... [ 13%]
tests/test_delay_model.py ........ [ 26%]
tests/test_history.py ......... [ 40%]
tests/test_lock_detector.py ........... [ 56%]
tests/test_params.py .......... [ 72%]
tests/test_phase_detector.py ...... [ 81%]
tests/test_simulator.py ........ [ 93%]
tests/test_state.py .... [100%]
======================================================= 65 passed in 0.03s ========================================================
まとめ
今回は、dll/history.py と tests/test_history.py を AI に起案させています。
これで、DLLSimulator から 履歴保存機能を独立した SimulationHistory クラスへ移行出来ました。次回は、入力クロックをクラス化します。