プログラミング・演習II 第 12 回 簡易版 論理シミュレータの作成 田向 まとめとしてのの応用課題 簡単なコードを読んで慣れ,理解を深める。 計算機の中に物を作り、動かす体験をする。 コードの追い方、考え方を練習する。 デバッグの仕方も習得する。 0からは難しいので、既に出来ているものを 修正していく。 自由に考えられるようになるには、時間が掛 かるので、少しずつ練習する。 簡易論理シミュレータの作成 - 要求仕様 - 論理ゲート: 2入力AND: and(出力、入力1、入力2,遅延); 2入力OR: or(出力、入力1、入力2,遅延); NOT: not(出力、入力1,遅延); NullNode: 回路への入力( 1/0, 遅延); 配線: wire(出力、入力); 時間: 整数時間 論理回路:論理ゲートをつないだもの verilogHDL をまねて module 回路名; イベント・シミュレータ: 離散的に、ある時間tで論理値が 1→0、0→1へと変化する。 値の変化は内部遅延で入力端子から出力端子へ “遅延”で伝わる。指定がない場合には遅延“0” simulator シミュレータ名 回路への入力は入力ポートに設定される。 Simulator の組み込み関数で実現 set(時間,値,入力ポート) 動作手順 1. シミュレーション対象を宣言する。 simulation sim; Node::SimObj = ∼ 2. 回路を宣言する。 module test1; 3. 回路使用する配線の宣言を行う。 wire i1, i2, i3; wire o1, o2, o3; wire o4, o5, o6; 4. 使用する素子を宣言する。 nullNode in1(o1, i1),in2(o2, i2), in3(o3, i3); and and1( o4, o1, o2 ,2); or or1 ( o5, o3, o4 ,1); not not1( o6, o5 ); 記述例 5.入力信号を設定する。 sim.set(0, 0, i1); sim.set(0, 0, i2); sim.set(0, 0, i3); sim.set(10, 1, i3); sim.set(25, 0, i3); sim.set(30, 1, i1); sim.set(35, 1, i2); 6.シミュレーションを行う。 for(int time=0; time< 100; time++) { sim.run(time); cout<< time <<" : "<< and1.out <<" , "<< or1.out<<" , " << not1.out<<"\n"; } タイムホイールとイベント イベント・キュー タイムホイール イベント登録 タイムスロット イベント実行 タイムホイール 時刻 時刻 素子 素子 値 値 時刻 時刻 素子 素子 値 値 時刻 時刻 時刻 素子 素子 素子 値 値 値 時刻 時刻 時刻 素子 素子 素子 値 値 値 プログラム 時刻 時刻 素子 素子 値 値 時刻 時刻 素子 素子 値 値 論理シミュレータの作成 論理素子: nodeから派生させる。 and → class and : public node {…} 回路: リスト、または,双方向リストで作る。 信号変化:イベントevent クラス 処理手順; 1. イベント・キューから値を取り出す。 2. 素子で入力値を論理演算する。 3. 演算結果が前の値と違った時にはイベント発生 4. イベント・キューにイベントを積む 5. イベント・キューが空ならば終了。空でなければ、 1へ戻る イベント・キュー:priority_queue で作る。 どうやって作る?既存の記述を利用する。 標準テンプレートライブラリmapを使用している。 既存のシミュレータ記述を利用して改変 Template(おさらい) 配列,stack,list:積む物が違うだけで 構造は同じ 特殊なテンプレート用の変数 T :不特定 旧版 <class T> , 新版 <typename T> Template<class T> class Array { T* a; int size; public: Array(int); T& operator [ ](int index) }; Template<class T> //コンストラクタArray<T>::Array(int s):a(new T[size=s]) { For( int I=0; I<size; I++) a[I]=0; } Template<T> T& Array<T>::operator[ ](int idx){ if(idx >=0 && idx <size) return a[idx]; return *new T; キューのテンプレートによる構成例 Template<class T> class queue{ enum {size = 10}; T *q[size]; int in, out; Public: queue():in(0),out(0){ } void add(T *I){ q[in++]=I; if(in>=size) in=0;} T*get(){if(out==in) return 0; T *result = q[out++]; if(out >=size) out=0; return result; } 使い方: クラス名 <パラメータ値> インスタンス名 queue <int> iq; queue <String> sq; テンプレートは複数の引数をもてる。 Template<class T, int sz> class buffer{ T v[sz]; .......… } 関数テンプレート例 #include <iostream> using namespace std; template <class X> void swapargs(X &a, X &b) { X temp; temp = a; a = b; b = temp; } int main( ){ int i=10, j=20; float x=10.1, y=23.3; cout << "オリジナル i, j: " << i << ' ' << j << endl; cout << "オリジナル x, y: " << x << ' ' << y << endl; swapargs(i, j); // 整数を入れ替える swapargs(x, y); // floatを入れ替える cout << "入れ替え後 i, j: " << i << ' ' << j << endl; cout << "入れ替え後 x, y: " << x << ' ' << y << endl; return 0; } 単純な汎用リンクリスト template <class data_t> class list { data_t data; list *next; public: list(data_t d); void add(list *node) {node->next = this; next = 0; } list *getnext() { return next; } data_t getdata() { return data; } }; template <class data_t> list<data_t>::list(data_t d) { data = d; next = 0; } int main(){ list<char> start('a'); list<char> *p, *last; ・・・・・・・・・・ } 汎用stackクラスの記述例 template <class StackT> class stack { StackT stck[SIZE]; int tos; // スタックの先頭の索引 public: void init() { tos = 0; } void push(StackT ch); StackT pop(); }; template <class StackT> void stack<StackT>:: push(StackT ob){ if(tos==SIZE) { cout << "スタックは一杯です\n"; return; } stck[tos] = ob; tos++; } Standard Template Library Standard Template Library: STL 標準テンプレート・ライブラリ 再利用可能なライブラリ を作る事がC++の主目標 STL例:map priorityQueueやBinaryTreeは同じKey(Index) を持つノードは一つだけ. mapは同じKeyを持つノードを いくつも持つことが出来る. 同時刻に動作するイベントは複数あるので このmapが必要! STL: map map <key, T, Compare, Allocator> 型 key のユニークなキーでインデックス付け、 型Tの格納されている 値へ高速にアクセス。 Compare: keyによる比較のための関数 Allocator: 値を置くノードの構造. map クラスのテンプレート template <class Key, class T, class Compare =less<Key> , class Allocator = allocator<pair<const Key, T>>> class map; typedef map< int , Edge* , less<int> > edgeMap; typedef map< int , Node * , less<int> > nodeMap ; ここで、Typedef は置き換え操作: edgeMap inEdge; は 置き換えにより map< int , Edge * , less<int> > inEdge; を意味している。 ただし,edgeMap が型定義であると通知(多分?) Edge /* nodeとnodeとを接続する配線.srcNodeから dstNodeへと方向を持っている有向枝である. */ class Edge{ Node* dstNode ; Node* srcNode ; public: //Edgeの値 0,1,2; 2は不定値Xを示す. /* シミュレーションでは0でも1でも無い値. 初期値として使用する.Xから0,1に変化する. また,1,0の値が衝突するとXになる. */ int EIN; Edge( ) { dstNode = srcNode =0; EIN=2;} ~Edge( ){ }; friend class Node; // Node, --- から friend class ------ // privateにアクセス出来る ..…. }; Node 必要なものは全て整理しておく.使う時に必要な物を 取り出して使用.メモリの節約とトレードオフの関係 にある. class Node{ public: int out; // 出力。初期値は不定 (2) edgeMap inEdge; // 出力エッジのリスト edgeMap outEdge; // 入力エッジのリスト int inEdgeCount; bool requestRun; static simulation* SimObj; Node( ):out(2), inEdgeCount(0),….,{ }; Node(Edge& o1, Edge& i1); Node(Edge& o1, Edge &i1, &i2); void outEdgeAdd( Edge& oE1){ …., outEdge.insert(edgeMap::value_type (outEdgeCount++, &oE1)); } virtual bool exec(int time, int svalue, Edge* e); }; 実際のmapの実装例(詳細:参考) map <key, T, Compare, Allocator> 型 key のユニークなキーでインデックス付け、 型T の格納されている 値へ高速にアクセス。 keyの比較に使うデフォルト演算: < 演算子 x :キー,y:格納される値 pair<const key x, T y> value_type インスタンス 両方向性反復子 /* binaryTree を思いだそう */ テンプレート引数 key と型T (t は T の値,u は T の const 値)。 ???? コピーコンストラクタ T(t) と T(u) デストラクタ t.~T( ) アドレス &t と &u(それぞれ T* と const T* に対応) 代入 t = a(a は T の値であり,定数の場合もある) テンプレート引数 Compare で使う型は,二項関数の 必要条件を満足しなければならない。 map(詳細:参考) template <class Key, class T, class Compare = less<Key> class Allocator = allocator<pair<const Key, T>> > class map { public: typedef Key key_type; typedef typename Allocator::pointer pointer; //Allocatorは型(class)である typedef T mapped_type; typedef pair<const Key, T> value_type; typedef Compare key_compare; . . . . . . . . . . . . . . . . . . . class iterator; map(詳細:参考) .............. ......... ... 関数オブジェクトは,定義された関数を呼び出す演 算子(( ) 演算子)を持つ。 関数コードの先頭アドレスで示す.データー領域もあ る.さらに,関数パラメータをつける.オブジェクト でも良い. class value_compare : public binary_function<value_type, value_type, bool> { friend class map<Key, T, Compare, Allocator>; protected : Compare comp; value_compare(Compare c): comp(c) {} public : bool operator( ) (const value_type&, const value_type&) const; }; コンストラクタ 反復子(参考) explicit map( ・・・・・・・・ const Compare& comp = Compare( ), const Allocator& alloc = Allocator( ) ); comp を使ってキーを順序付ける(comp が与えられ た場合)空のマップを構築する。このマップはアロ ケータ alloc を使ってすべてのストレージ管理を行 う。 mapはbinaryTreeのnodeにallocで pair<const Key, T> を割り付けていることを言っている. map(tree)のノードを渡って移動するpointer(index) がほしい. map::iterator i; と宣言して,i++の様に表記し て使いたい. Iterator begin( ); マップ内に格納されている最初の要素を指す反復子 を返す。「最初」はマップの比較演算子 Compare によって定義される。 iterator end(); マップ内に格納されている最後の要素(言い換えれ ば,off-the-end 値)を指す反復子を返す。 シミュレータのコード このmapを使って作成してある. typedef map< int , Edge* , less<int> > edgeMap; typedef map< int , Node * , less<int> > nodeMap ; Net、 module, And class net{ private: int nodeCount; // net 内の Node の数 nodeMap node ; // net に属する Node public: net( ); void addNode(Node* nodep); } // net クラスを module クラスという名前で利用するた め。verilog 言語との対応 class module:public net{ public: module(){ motherNet = this; } }; class And: public Node{ public: And(Edge& o1, Edge& i1, Edge& i2): Node(o1,i1,i2, and){ } // exec 関数のオーバーライド bool exec(int time,int svalue, Edge* e); }; Or, Not, input も同様 Andの実行部 bool And::exec(int time,int sv, Edge* e){ e->EIN = sv; // Pin の値をセット out =1; // 全ての Pin をスキャンするためのイテレータ edgeMap::iterator eitr; for ( eitr = inEdge.begin(); eitr != inEdge.end(); eitr++ ) { // 入力に 2 が一つでもあれば, if(eitr->second->EIN ==2 ) { out = 2; break; // for 文を抜け出す ok? }else if(pitr->second->EIN ==0 ){ // 入力に 0 が一つでもあれば, out = 0; break; // for 文を抜け出す } } // 基底クラス Nodeの exec 呼びだし return( Node::exec( time, svalue, e ) ); } Base class Nodeの実行部 And,Or, Not, 全ての素子が実行する bool Node::exec(int time, int , Pin*) { if(!requestRun){ requestRun = true; SimObj->scheduleEvent( new deliverEvent(time+1,this)); } return( false ); // ok? } scheduleEvent タイムスロット exec プログラム タイムホイール 時刻 時刻 素子 素子 値 値 時刻 時刻 素子 素子 値 値 時刻 時刻 時刻 素子 素子 素子 値 値 値 時刻 時刻 時刻 素子 素子 素子 値 値 値 時刻 時刻 素子 素子 値 値 時刻 時刻 素子 素子 値 値 Simulation.h 信号変化イベントの定義 イベントのbase class class event{ public: unsigned int time ; // イベント発生時刻 simulation* SimObj; event(unsigned int t): time(t){ } // 純粋仮想関数 virtual void processEvent ()=0; }; struct eventComparison{ bool operator () (event * left, event * right) { return left->time > right->time; } }; ( )があるから関数オブジェクト 実行イベント 派生イベント: 論理素子の実行による値変化のイベント class execEvent : public event{ private: Node* NodeObj; // イベント処理体操のNode int svalue; // イベントによって伝搬される値 Edge* edge; // イベントが到達する edge public: // コンストラクタ execEvent( unsigned int time, int sv , Edge* e, Node* destNode ) : event(time), NodeObj(destNode), svalue(sv), edge(e) { } void processEvent( ); // イベントの処理 }; Simulation class I シミュレータのオブジェクト宣言 class simulation { protected: //優先順位キューの宣言 priority_queue <event *, vector<event *>, eventComparison > eventQueue; public: unsigned int time; unsigned int eventID; // イベントの ID simulation* SimObj; // コンストラクタ simulation () : eventQueue(), time(0) , eventID(0) { SimObj = this; } Simulation class II // イベントの登録,値設定などシミュレーショ ンで使う処理のインターフェース void scheduleEvent (event * newEvent); void set(int t, int v, Edge& e){ scheduleEvent(new execEvent(t, v, &e, e.dstNode)); } // 各時刻における論理値の計算 void run(unsigned int); // 結果の伝搬 void deliver(unsigned int t, Node* nobj); }; シミュレーションの実行部 I void simulation::run(unsigned int cTime) { // 優先順位キューに登録されたイベントを スキャン while( !eventQueue.empty()) { event * nextEvent = eventQueue.top(); time=nextEvent->time; if( time >= cTime ) break; // while 文 を抜ける nextEvent->processEvent(); // process を実行 // 次のイベントを取り出す (ポップする) eventQueue.pop(); delete nextEvent; } } シミュレーションの実行部 I-2 (註) このeventQueue では異なる時刻の eventはqueueされるが,同じ時刻のevent はstackされている. したがって,同時刻のイベントを取り出し て実行すると,順序が逆になってしまう. STLのmap:同じKeyの時には,積む順序の 区別まで必要のない問題を想定して作っ てある. シミュレーションの実行部 II void simulation::scheduleEvent (event * newEvent){ newEvent->SimObj = SimObj; // イベントを優先順位キューへ登録 eventQueue.push ( newEvent); } //純粋仮想関数の実装 void execEvent::processEvent( ){ NodeObj->exec(time, svalue, pin ); } - 同時刻イベントの順序の問題 - // priority_queue <event *, vector<event *>, eventComparison > eventQueue への単 純なpushではうまくいかない. シミュレーションの実行部 II-2 不具合点の解決法 (1) stackされないようなqueue を作る.i.e., 同時刻でもqueue動作をするように eventQueue をつくる. (2) 取り出し方を工夫する. (3) queueへの積み方を工夫する. (4) 時間の他に順序付けの変数を加える.た とえば,同時刻のeventでもeventID で順 番をつけて,eventQueue につむ. など,色々ある. どれが,早いかとか,簡単かで選択する. このsimulatorコードの記述と動作 int main(void){ simulation sim; Node::SimObj = ∼ //回路ネットの生成 module クラス (net クラスの別名) のインスタンス module test1; Edge i1, i2, i3; ・・・・・ NullNode null1(o1, i1); ・・・・・ And and1( o4, o1, o2 ); ・・・・・ sim.set(0, 0, i1); ・・・・・ for(int time=0; time< 100; time++) { sim.run(time); } 0.0 Gate constructorはnetに登録する. 同時にgate間の接続も行う. 0.1 シミュレータを生成して,対象をシミュ レータに教える. イベントが処理されると新たなイベントが 発生する。 • Run()→ exec() 1. そのイベントを登録して処理する。 • Deriver() 2. これを繰り返してイベントが無くなるまで続 ける。 3. 最初はイベントを登録しなければならない。 4. eventSet()関数が必要。 試験: 7/27(金) 1. 試験は授業内容全般とする. 2. 最終レポート締切りは8月3日(金)14時と する.5号館509号室田向まで提出の事. 3.単位取得を望むものは,試験,最終レ ポートの両方が必要. 4.最終レポートについては,いつでも質問 に来ること.TAが対応する予定. まとめ 1. C++,オブジェクト,OOPによる作成法を 説明. 2. 代表的なデータ構造,list,stack,queue, binaryTreeを作成し使い方を説明した. 3. しかし,アルゴリズムは時間不足,今後勉 強してほしい. 4. Cでも作れる.けれどC++. それとも, Java,C#? Lisp,prologもある.さらに, systemC,specC, verilog, VHDLなど もある. 5. programのcoding,systemのdesign? 複雑な物を簡単に作る技術? 複雑なシステムはそれでも複雑? その設計をどう評価する. あるいは,どう評価される? • 今後のシステムはハードとソフトとがさら に,複雑に入り組んでいる.それが設計 出来ないと,製造技術だけがあっても何 も作れない.
© Copyright 2025 ExpyDoc