Posts Tagged with "BSV"

既に発行済みのブログであっても適宜修正・追加することがあります。
We may make changes and additions to blogs already published.

Pongと強化学習 (98)

posted by sakurai on May 15, 2025 #973

以下のコードの説明をお願いします。

    q_network = QNetwork(envs).to(device)
    optimizer = optim.Adam(q_network.parameters(), lr=args.learning_rate)
    target_network = QNetwork(envs).to(device)
    target_network.load_state_dict(q_network.state_dict())

このコードは、Deep Q-Network (DQN) の実装の一部であり、以下の各ステップで特定の機能を実行しています。DQNは強化学習において、エージェントが最適な行動を学習するための一手法であり、ニューラルネットワークを用いて行動価値関数(Q値)を近似します。

1. Qネットワークの初期化:
        q_network = QNetwork(envs).to(device)

この行では、環境envsに基づいたQ値を推定するためのニューラルネットワーク(QNetworkクラスのインスタンス)を作成し、計算を行うデバイス(CPUまたはGPU)にモデルを移動しています。to(device)は、モデルを指定されたデバイス(例えばGPU)に配置し、計算の効率を向上させるために使用されます。

2. オプティマイザの設定:
        optimizer = optim.Adam(q_network.parameters(), lr=args.learning_rate)

ここでは、作成したQネットワークのパラメータを最適化するために、Adamオプティマイザを設定しています。lr=args.learning_rateは学習率を指定しており、これによりネットワークの学習プロセスの速度や収束の具合が調整されます。

3. ターゲットネットワークの初期化:
        target_network = QNetwork(envs).to(device)

DQNでは、学習の安定化のためにターゲットネットワークを使用します。この行では、元のQネットワークと同じ構造の新しいネットワークを作成し、同じデバイスに配置しています。ターゲットネットワークは、学習中に定期的にQネットワークから重みをコピーすることで更新されますが、その更新はQネットワークよりも頻繁ではありません。

4. ターゲットネットワークへの重みのコピー:
        target_network.load_state_dict(q_network.state_dict())

この行で、ターゲットネットワークに対してQネットワークの重みをコピーしています。これにより、両方のネットワークが学習開始時点で同じパラメータを持つことになります。ターゲットネットワークの重みは、一定の間隔でQネットワークから更新され、学習中の価値推定の安定性を保ちます。

これらのステップは、DQNの学習プロセスの基盤を構築しており、エージェントが与えられた環境内で最適なポリシーを効率的に学習するための準備を整えています。


左矢前のブログ 次のブログ右矢

Pongと強化学習 (97)

posted by sakurai on May 13, 2025 #972

Q-networkのpythonコードの各ステージをbsvに変換する予定です。一方、Q値からアクションを取り出す関数は何で、どのようにbsvに変換するのでしょうか?


Q-networkは主にAtariゲームのような画像ベースの入力を処理するために設計されており、各層(畳み込み層と全結合層)が特定の画像特徴を抽出し、最終的に行動空間のサイズに合わせた出力を生成します。

Q値からアクションを取り出すプロセス(Python)

Q-networkからアクションを選択するプロセスは、通常、Q値の配列の中で最大値を持つインデックスを選択することで行われます。これはPythonで以下のように表現されます(例としてPyTorchを使用):

    q_values = network(observation)
    action = q_values.argmax().item()

BSV(Bluespec SystemVerilog)への変換

BSVでこのプロセスを実装するには、いくつかのステップが必要です。まず、ニューラルネットワークの各層をBSVで実装する必要があります。次に、Q値の最大値を選択するロジックをBSVで記述します。

1. ネットワーク層の実装

BSVでは、畳み込み層や全結合層をカスタムモジュールとして実装することができます。例えば、畳み込み層をモジュールとして定義し、そのパラメータ(フィルタのサイズ、ストライド、パディング等)を指定します。全結合層も同様に、入力サイズと出力サイズをパラメータとしてモジュールを定義します。

    module mkConvolutionLayer#(parameter Int filterSize, Int numFilters, Int stride)(...);
        // 畳み込み層の実装
    endmodule
    
    module mkFullyConnectedLayer#(parameter Int inputSize, Int outputSize)(...);
        // 全結合層の実装
    endmodule

2. アクション選択ロジック

Q値から最大値を選択するモジュールは、入力としてQ値の配列を受け取り、最大値のインデックスを出力として返すように設計されます。これは、繰り返し比較と条件分岐を使用して実装できます。

    module mkSelectAction#(parameter Int numActions)(Vector#(numActions, Float) qValues) -> Action;
        Action action = 0;
        Float maxQValue = qValues[0];
        for (Integer i = 1; i < numActions; i++) {
            if (qValues[i] > maxQValue) {
                action = i;
                maxQValue = qValues[i];
            }
        }
        return action;
    endmodule

このように、BSVを使用してPythonで定義されたニューラルネットワークとその動作をFPGA向けに再設計し、具体的なハードウェアアクセラレーションを実現することが可能です。


左矢前のブログ 次のブログ右矢

Pongと強化学習 (96)

posted by sakurai on May 12, 2025 #971

例えばシリアル通信から来る、ジョイスティックを右に倒した事象をスイッチに見せかけるような回路を作成してください。


FPGAでUARTを介して特定のコマンドを受け取り、それをジョイスティックの動作に見せかけるようなスイッチ動作をエミュレートする回路を設計するためには、いくつかのステップが必要です。ここでは、Bluespec SystemVerilog (BSV) を使用してそのようなモジュールを設計する方法について説明します。

ステップ 1: UARTインターフェースの設定

まず、FPGAに既に設計されているUARTインターフェースを使用して、シリアルデータを受け取る必要があります。このUARTモジュールは、受信したデータを内部バスや処理モジュールに渡す役割を担います。

ステップ 2: コマンド解析ロジックの作成

受け取ったデータ(コマンド)を解析し、それが「右」や他のジョイスティックの動きに対応するコマンドであるかを判断するロジックを作成します。

ステップ 3: スイッチエミュレーションモジュールの設計

コマンドに基づいて、ジョイスティックのスイッチが操作されたかのような信号を生成するモジュールを設計します。このモジュールは、外部デバイスや他のFPGA内モジュールがジョイスティック入力として解釈できるような信号を出力します。

サンプルコード (Bluespec SystemVerilog)

以下は、これらのステップを実装するための基本的なBSVモジュールの例です。この例では、単純なコマンド解析とスイッチエミュレーションを示しています。

    import GetPut::*;  // Get, Put インターフェースを利用
    
    // UARTからのデータを受け取るモジュール
    module mkUART_Receiver(Ifc_UART);
        // UARTのデータ受信関連のロジック
        // ...
    
        rule receive_data;
            // UARTからデータを受け取る
            let cmd = uart.receive();
    
            // コマンドが「右」の場合、関連するアクションを実行
            if (cmd == "右") {
                // スイッチエミュレーション信号を生成
                joystickRightAction();
            }
        endrule
    
        method void joystickRightAction();
            // ジョイスティックの「右」動作に対応する信号を出力
            // ...
        endmethod
    endmodule

ステップ 4: テストとシミュレーション

設計したモジュールの動作を検証するために、シミュレーションを行い、さまざまなコマンドが適切に解析され、期待通りのスイッチ動作がエミュレートされるかを確認します。

この基本的なフレームワークを参考にして、より複雑なジョイスティックのエミュレーションや他の機能を拡張することができます。必要に応じて、エラー処理やその他のロバストな機能を追加してください。


左矢前のブログ 次のブログ右矢

Pongと強化学習 (95)

posted by sakurai on April 2, 2025 #969

インベーダゲームの報酬設計はどのようにしたら良いですか?


インベーダーゲームで移動に対しても微小なペナルティを適用することは、エージェントが無駄な動きを避け、より効率的な戦略を学習するために有用です。このペナルティを適切に設定することで、エージェントは必要な時にのみ移動し、より多くのインベーダーを撃破することに集中するようになります。以下に、移動に対する微小なペナルティを含む報酬設計のアプローチを説明します。

移動ペナルティの追加

  1. ペナルティの設定:

    • 移動に対するペナルティは小さく設定することが一般的です。例えば、移動するたびに -0.01 などの値を適用することが考えられます。これは、移動が基本的には許容されるが、できるだけ少ない移動で高い効率を達成することが望ましいためです。
  2. 報酬関数の更新:

    • 移動したことを検知し、それに応じて報酬から微小な値を引くように報酬関数を更新します。このとき、他の報酬要素(インベーダー撃破の得点や自機破壊のペナルティ)とのバランスを考える必要があります。

コード例

以下の関数get_rewardは、インベーダーの撃破、自機の破壊、そして移動に対する報酬を計算し、それを正規化する例を示しています。

    def normalize_reward(score, max_score=30, min_score=-1):
        # 最大得点で正規化
        return max(min(score / max_score, 1), -1)
    
    def get_reward(invader_type, player_hit, moved):
        base_scores = {1: 10, 2: 20, 3: 30}
        reward = 0
    
        # インベーダー撃破の得点を追加
        if not player_hit:
            reward += base_scores.get(invader_type, 0)
        
        # 自機が破壊された場合のペナルティ
        if player_hit:
            reward -= 100
        
        # 移動した場合の微小なペナルティ
        if moved:
            reward -= 0.01  # 移動するたびに少しのペナルティを適用
    
        # 報酬の正規化
        normalized_reward = normalize_reward(reward)
        return normalized_reward

ペナルティの影響の監視

この報酬設計を実装した後、エージェントの行動パターンを注意深く監視し、移動ペナルティが望ましい効果をもたらしているかどうかを評価することが重要です。ペナルティが高すぎると、エージェントが必要なときに移動を避けるようになり、パフォーマンスが低下する可能性があります。一方で、ペナルティが低すぎると、エージェントが無駄に多く移動し続ける可能性があります。そのため、適切なペナルティの設定とその効果の評価が、成功への鍵となります。


左矢前のブログ 次のブログ右矢

Pongと強化学習 (94)

posted by sakurai on April 1, 2025 #968

エピソードとは何でしょうか?自機3台が1エピソードと考えてよいですか?


強化学習における「エピソード」とは、エージェントが環境との相互作用を開始してから終了するまでの一連のステップまたは遷移のことを指します。エピソードの終了は通常、特定の終了条件が満たされたときに発生します。これには、目標の達成、最大ステップ数の到達、またはゲーム内での「失敗」状態(例えば、全てのライフを失うなど)が含まれます。

エピソードの定義

エピソードの具体的な定義は、対象となるタスクやゲームによって異なります。エピソードは以下の要素を含みます:

  • 開始:エージェントが環境の初期状態から開始します。
  • ステップ:エージェントがアクションを選択し、環境がそのアクションに基づいて状態を更新し、報酬を提供します。
  • 終了:ゲームオーバー、目標達成、または最大ステップ数に到達したときにエピソードが終了します。

自機3台が1エピソードか

具体的なゲーム(例えば、アーケードゲームの「スペースインベーダー」など)において自機が3台ある場合、1エピソードをどのように定義するかは、学習の目的によります。自機1台が失われるごとにエピソードをリセットすると、エージェントは一台失うごとのリカバリー戦略を学ぶことができますが、全機失うまでを一つのエピソードと定義すると、より長期的な戦略を学習することになります。

自機3台が全滅するまでを1エピソードと考える場合: - 利点:エージェントはより長い期間にわたる戦略を学習することができ、自機をいかに効果的に保つか、どのようにして最大限のスコアを出すかといった戦略を考えることができます。 - 欠点:自機が一度に失われた場合、特に序盤で失敗すると、そのエピソードでの学習効果が少なくなる可能性があります。

エピソードを自機1台ごとに区切る場合: - 利点:短いエピソードによって、特定の状況に対する反応を素早く学習することができます。また、失敗からの回復やその場での戦術的な判断を重視する戦略を学ぶことが可能です。 - 欠点:全体的なゲームの進行や、自機を長持ちさせる戦略など、より長期的な目標を達成するための戦略がおろそかになるかもしれません。

結論

エピソードの定義は、学習させたい行動や戦略に依存します。エージェントがゲーム全体の進行を理解し、長期戦

略を学習することが目標であれば、自機3台全滅をもって1エピソードとすることが適切です。これにより、より総合的なゲームプレイ戦略を開発するためのデータが提供されます。


左矢前のブログ 次のブログ右矢

Pongと強化学習 (93)

posted by sakurai on March 31, 2025 #967

self.env.stepの中でどのようにアクションを判定しているのですか?


self.env.step メソッドの動作を理解するには、まず OpenAI Gym の環境のフレームワークと、具体的にどのようにアクションが環境の状態に適用されるかを考える必要があります。step メソッドは、ゲームやシミュレーションの各ステップで中核となる関数です。この関数は、与えられたアクションを受け取り、そのアクションに基づいて環境の状態を更新し、新しい状態、報酬、エピソードが終了したかどうかのフラグ、および追加情報を返します。

env.step(action) の基本的な動作

  1. アクションの受け取り: 関数はアクションを引数として受け取ります。このアクションは通常、整数(Discrete スペースの場合)、実数のベクトル(Box スペースの場合)、またはその他の形式(MultiDiscrete, MultiBinary など)で表されます。

  2. アクションの適用: 環境はこのアクションを現在のゲームまたはシミュレーションの状態に適用します。アクションが何を意味するか(例えば移動、ジャンプ、射撃など)は、環境の設計に依存します。たとえば、AtariゲームのSpace Invadersでは、アクションには「左に移動」、「右に移動」、「射撃」などが含まれます。

  3. 状態の更新: アクションが適用されると、環境はその結果に基づいて新しい状態を生成します。これには、プレイヤーの位置、敵の位置、スコアなどが含まれる場合があります。

  4. 報酬の計算: 新しい状態に基づいて報酬が計算されます。報酬は、アクションの結果として得られるポイントや、ゲームのルールに基づく他のメトリックによって決定されます。

  5. 終了フラグと追加情報: ゲームが終了したかどうかのフラグ(例えばプレイヤーの命が尽きた、目標を達成した等)と、デバッグや詳細分析に役立つ追加情報が生成されます。

実装の例

以下に、カスタム環境の簡単な step メソッドの実装例を示します。この例では、アクションが環境にどのように適用され、報酬がどのように計算されるかを示しています。

    class CustomEnv(gym.Env):
        def __init__(self):
            super(CustomEnv, self).__init__()
            self.action_space = gym.spaces.Discrete(3)  # 例: 0=左, 1=右, 2=射撃
            self.state = None
            self.done = False
    
        def step(self, action):
            if action == 0:
                # 左に移動する処理
                pass
            elif action == 1:
                # 右に移動する処理
                pass
            elif action == 2:
                # 射撃する処理
                pass
    
            # 状態の更新、報酬の計算、
    
    ゲーム終了のチェック
            reward = 0
            self.done = self.check_if_game_over()
            return self.state, reward, self.done, {}
    
        def reset(self):
            # 環境を初期状態にリセット
            self.state = self.initialize_state()
            self.done = False
            return self.state
    
        def check_if_game_over(self):
            # ゲーム終了条件の確認
            return False
    
        def initialize_state(self):
            # 初期状態の設定
            return None

このように、step メソッドはアクションを受け取り、それに基づいて環境の状態を更新し、結果として新しい状態と報酬を返す役割を果たします。このプロセスはゲームやシミュレーションの種類によって大きく異なる場合がありますが、基本的なフローは同じです。


左矢前のブログ 次のブログ右矢

Pongと強化学習 (92)

posted by sakurai on March 28, 2025 #966

これだと5, 6は移動と射撃が複合されており、移動だけにマイナスのペナルティをつけても射撃につけられたとAIが誤解してしまうので、分離したいのですが。


左右の移動にのみ極小のペナルティを適用するためには、カスタム環境のstepメソッド内で報酬にペナルティを加える処理を追加する必要があります。以下は、CustomSpaceInvadersクラスを修正して、左右の移動に小さなペナルティを適用する方法を示したコードです。

移動アクションへのペナルティの適用

    import gym
    from gym.spaces import MultiBinary
    import numpy as np
    
    class CustomSpaceInvaders(gym.Wrapper):
        def __init__(self, env):
            super(CustomSpaceInvaders, self).__init__(env)
            # アクションスペースを MultiBinary に設定
            # 3つのアクション: [左移動, 右移動, 射撃]
            self.action_space = MultiBinary(3)
            self.move_penalty = -0.01  # 左右移動のペナルティ
    
        def step(self, action):
            # マルチバイナリアクションを元の環境のアクションに変換
            original_action = 0
            if action[0] == 1:  # 左移動
                original_action = 3
            if action[1] == 1:  # 右移動
                original_action = 2
            if action[2] == 1:  # 射撃
                original_action = 1
            if action[0] == 1 and action[2] == 1:  # 左移動 + 射撃
                original_action = 5
            if action[1] == 1 and action[2] == 1:  # 右移動 + 射撃
                original_action = 4
            
            # 環境を進める
            obs, reward, done, info = self.env.step(original_action)
    
            # 左または右に移動した場合にペナルティを適用
            if action[0] == 1 or action[1] == 1:
                reward += self.move_penalty
    
            return obs, reward, done, info
    
    # オリジナルの環境をラップ
    env = gym.make('SpaceInvaders-v0')
    custom_env = CustomSpaceInvaders(env)
    
    # ゲームを開始
    obs = custom_env.reset()
    done = False
    while not done:
        # ランダムなアクションを生成
        action = custom_env.action_space.sample()
        obs, reward, done, info = custom_env.step(action)
        print(f"Action: {action}, Reward: {reward}")
    
    custom_env.close()

コードの解説

  • ペナルティの設定:クラス内にmove_penalty属性を追加し、その値を左右の移動に適用しています。この値は負の小さな数で、移動するたびに報酬から差し引かれます。
  • 報酬の調整:左または右に移動するアクションが選択された場合、rewardにペナルティを加算します(負の値を加算するため、実際には報酬が減少します)。

このようにして、エージェントは左右に移動する際に小さなコストがかかることを学習し、可能であれば移動を避ける戦略をとるようになるかもしれません。ただし、ペナルティの値をどの程度に設定するかは、タスクの性質や学習の目標に応じて慎重に選ぶ必要があります。


左矢前のブログ 次のブログ右矢

Pongと強化学習 (91)

posted by sakurai on March 27, 2025 #965

atariのspace invadersでのenvとのインタフェースで、アクションはどのようにenvに与えていますか?例えばアクションは、左、右、FIREとします。


Atariゲームである「Space Invaders」において、OpenAI Gym 環境を使用する場合、アクションスペースは通常、Discreteタイプで定義されています。この環境では、プレイヤーは一度に1つのアクションのみを実行できますが、そのアクションはプレイヤーが同時に移動と射撃を行うことを可能にするように設計されている場合があります。

Space Invadersのアクションスペース

Space Invadersのアクションセットは次のようになります:

  1. NOOP (何もしない)
  2. FIRE (射撃のみ)
  3. RIGHT (右に移動)
  4. LEFT (左に移動)
  5. RIGHTFIRE (右に移動しながら射撃)
  6. LEFTFIRE (左に移動しながら射撃)

ここで、RIGHTFIRELEFTFIREは移動と射撃を組み合わせたアクションであり、プレイヤーが一度のアクション選択で移動と射撃を同時に実行できることを示しています。

Gym 環境でのアクション実装例

以下の例では、OpenAI Gym の「Space Invaders」環境を初期化し、いくつかのアクションを実行する方法を示します。アクションはランダムに選択されますが、実際の利用時にはエージェントの方策に従って適切なアクションが選択されます。

    import gym
    
    # 環境の作成
    env = gym.make('SpaceInvaders-v0')
    env.reset()
    
    # アクションの意味を表示
    print("Action meanings:", env.unwrapped.get_action_meanings())
    
    # いくつかのステップを実行してみる
    for _ in range(10):
        action = env.action_space.sample()  # ランダムにアクションを選択
        observation, reward, done, info = env.step(action)  # 選択したアクションを環境に適用
        print("Selected action:", action)
        if done:
            env.reset()
    
    env.close()

このコードはSpace Invadersゲームを初期化し、利用可能なアクションを確認し、ランダムにアクションを選択してゲームを進行させます。get_action_meanings()メソッドによりアクションの意味を表示し、実際のゲームプレイでどのようにアクションが適用されるかを観察できます。

まとめ

Space InvadersのようなAtariゲームでは、一般的なDiscreteアクションスペース内で複数の動作を組み合わせるアクション(例えば、移動しながら射撃)を提供することが一般的です。これにより、プレイヤーはより戦略的にゲームを進めることが可能になります。


左矢前のブログ 次のブログ右矢

Pongと強化学習 (90)

posted by sakurai on March 21, 2025 #963

TbのBSVソースを示します。

Tb.bsv:

    import StmtFSM::*;
    import DumpFSM::*;
    import Uart::*;
    import RegFile::*;
    import Connectable::*;
    
    typedef Bit#(16) Addr;
    typedef Bit#(4) Data;
    
    `define STX 8'h02
    
    (* synthesize *)
    module mkTb();
       RegFile#(Addr, Data) vram <- mkRegFileLoad("data.hex", 0, 256*256-1);
       Reg#(Bool) cwait <- mkReg(False);
       Reg#(Bool) game <- mkReg(True);
       Reg#(Bool) tic <- mkReg(False);
         
       DumpFSM_ifc dump <- mkDumpFSM();
       Uart_ifc uart <- mkUart();
       
       mkConnection(cwait, dump.cwaitm);
       mkConnection(dump.datam, vram.sub(dump.addrm));
       mkConnection(tic, dump.c60Hz);
       mkConnection(game, dump.mode);
       mkConnection(uart.sout, dump.sin);
       mkConnection(uart.sin, dump.sout);
    
       Stmt test =
       seq
          // Game Mode: ticをスルーする
          game <= True;
          cwait <= False;
          repeat(100) noAction;
          // 非Game Mode: GameFSMに停止要求を出して停止したらダンプする
          game <= False;
          uart.write(8'h31);
          uart.write(8'h32);
          uart.write(8'h33);
          uart.write(`STX); // ここからダンプスタート
          uart.write(8'h31);
          uart.write(8'h32);
          uart.write(8'h33);
          cwait <= True; // GameFSMが停止したシミュレーション
          repeat(20) uart.write(8'h31);
          repeat(100) noAction;
          game <= True;
          await(!dump.selm()); // DumpFSMが動作完了し、SELをOFF
          repeat(100) noAction;
          $finish;
       endseq;
    
       mkAutoFSM(test);
    
        // クロックのトグルをカウントするレジスタ
        Reg#(Bit#(4)) counter <- mkReg(0);
        // カウンターが特定の値に達したらクロックをトグル
        rule toggleClock;
            if (counter == 5) begin // ここで分周比を調整
                tic <= !tic;
                counter <= 0;
            end else begin
                counter <= counter + 1;
            end
        endrule
        
    endmodule

左矢前のブログ 次のブログ右矢

Pongと強化学習 (89)

posted by sakurai on March 20, 2025 #962

BSVにより、DumpFSMを改造します。従来のDumpDSMの仕様は、

  • SW等の外部指示により、GameFSMにウエイト要求を出す
  • GameFSMはSyncWait時にウエイト信号(停止中信号)を出力する
  • DumpFSMは停止中信号を確認し、SELをTrueとする
  • VRAMの内容をUARTで出力する
  • 完了したらSELとウエイト要求をFalseにする

という動作でした。今回改造するポイントは外部指示ではなく、強化学習ソフトウエアの指示により画面ダンプを実施する点です。そのために従来送信部のみであったUARTを受信部も作成します。

  • SW等の外部指示により、GameモードがFalseになる
  • シリアルデータがSTXを指示したらGameFSMにwait要求を出す
  • GameFSMはSyncWait時にウエイト信号(停止中信号)を出力する
  • DumpFSMは停止中信号を確認し、SELをTrueとする
  • SELがTrueの間は60Hz信号を遮断する。これによりGameFSMが進むことは無い
  • VRAMの内容をUARTで出力する
  • 完了したらSELとウエイト要求をFalseにする。
  • GameモードがTrueになったら60Hz信号を再開する。

図962.1に構造図を示します。DumpFSMモジュールには送信と受信のUARTを配置し、それをテストするテストベンチ内にも同じUARTを対向して配置します。

図%%.1
図962.1 ブロック図

DumpFSM.bsv

    import StmtFSM::*;
    import Uart::*;
    import Connectable::*;
    
    typedef Bit#(16) Addr;
    typedef Bit#(4) Data;
    typedef Bit#(8) Byte;
    
    `define STX 8'h02
    
    interface DumpFSM_ifc;
       (* prefix="" *)
       method Action datam(Data newdata); // input
       (* prefix="" *)
       method Action cwaitm(Bool newcwait); // input
       method Bool sreqm(); // output
       method Addr addrm(); // output
       method Bool selm(); // output
       method Bit#(1) sout(); // serial output
       (* prefix="" *)
       method Action sin(Bit#(1) nidata); // serial input
       (* prefix="" *)
       method Action c60Hz(Bool newtic); // input
       method Bool ticfunction(); // output
       (* prefix="" *)
       method Action mode(Bool newgame); // input
    endinterface
    
    (* synthesize, always_ready="sreqm, addrm, selm, sout, ticfunction",
                   always_enabled="datam, cwaitm, sin, c60Hz, mode" *)
    module mkDumpFSM(DumpFSM_ifc);
    
       Reg#(Addr) addr <- mkReg(0);
       Reg#(Data) data <- mkRegU;
       Reg#(Byte) byteData <- mkRegU;
       Reg#(UInt#(9)) x <- mkRegU;
       Reg#(UInt#(9)) y <- mkRegU;
       Reg#(UInt#(4)) i <- mkRegU;
       Reg#(Bool) cwait <- mkReg(False);
       Reg#(Bool) sreq <- mkReg(False);
       Reg#(Bool) sel <- mkReg(False);
       Reg#(Bool) tic <- mkReg(False);
       Reg#(Bool) game <- mkReg(True);
       
       Uart_ifc uart <- mkUart();
    
       function Stmt nibbleOut(Data nibble);
          return (seq
             byteData <= extend(nibble) + ((nibble >= 10) ? (-10 + 8'h61) : 8'h30);
             uart.write(byteData);
             $write("%c", byteData);
          endseq);
       endfunction: nibbleOut
    
       Stmt test = seq
           while (True) seq
               await(!game); // Gameでない場合、トリガが来たら吸出し
               await(uart.read() == `STX);
               sreq <= True; // 描画マスタ停止要求を出す
               await(cwait); // 描画マスタ停止したら、
               sel <= True; // ダンプ開始
               for (i <= 1; i <= 4; i <= i + 1) seq
                   uart.write(8'h38); // start of header '8888' -> remove
                   $display("8");
               endseq
               addr <= 0;
               for (y <= 0; y <= 2/* 255 */; y <= y + 1) seq
                   for (x <= 0; x <= 255; x <= x + 1) seq
    //                 $display("%3d, %3d", x, y);
    //                 $display(" %04x ", addr);
                       action
                          addr <= addr + 1;
                       endaction
                       nibbleOut(data);
                   endseq // for x
                   uart.write(8'h0a); // LF
               endseq // for y
               await (uart.done()); // 送信終了待ち
               action
                   sreq <= False; // 要求OFF
                   sel <= False; // 選択OFF
               endaction
          endseq // while
       endseq; // Stmt
    
       mkAutoFSM(test);
    
       method Bool sreqm(); // output
          return sreq;
       endmethod
       method Addr addrm(); // output
          return addr;
       endmethod
       method Action datam(Data newdata); // input
          data <= newdata;
       endmethod
       method Action cwaitm(Bool newcwait); // input
          cwait <= newcwait;
       endmethod
       method Bool selm(); // output
          return sel;
       endmethod
       method Bit#(1) sout(); // serial output
          return uart.sout();
       endmethod
       method Action sin(Bit#(1) nidata); // serial input
          action
             uart.sin(nidata);
          endaction
       endmethod
       method Action c60Hz(Bool newtic); // input
          if (game && !sel) action
             tic <= newtic;
          endaction
       endmethod
       method Bool ticfunction; // output
          return tic;
       endmethod
       method Action mode(Bool newgame); // input
          game <= newgame;
       endmethod
    
    endmodule

左矢前のブログ 次のブログ右矢


ページ: