0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Python を使った PLL/DLL 設計手法(11)

0
Posted at

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 が、次の二つの責務を持っています。

  1. シミュレーションを進行する
  2. 各サイクルの結果を記録する

この記録責務を分離するので、dll/history.pytests/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
        )

ここで data を @property にしています。 @property にすることで、利用側は history.data と書くことが出来ます。通常はhistory.data()になります。

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.pytests/test_history.py を AI に起案させています。

これで、DLLSimulator から 履歴保存機能を独立した SimulationHistory クラスへ移行出来ました。次回は、入力クロックをクラス化します。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?