scilabで遊ぼう (7) [プログラム三昧]
前回、連続時間伝達関数を離散時間伝達関数に変換した際に伝達関数にサンプリング周期の情報が入ったことがわかりました。 すると、後からサンプリング周期情報を入れることが出来るのかな?
離散時間伝達関数にサンプリング周期情報を入れる
-->z=poly(0,'z'); -->sys=syslin(50d-6,(1+z^(-1)+z^(-2)+z^(-3)+z^(-4))/5); -->sys("dt") ans = 0.00005
指定したサンプリング周期は、"dt"というインデックスで参照することが出来ます。 この状態でボード線図を描くと、絶対周波数で表示されるようになります。
-->scf(1);clf;bode(sys,1,1e4);
また、サンプリング周期を後から変更することもできちゃいます。
-->sys("dt")=1e-3;
これにて、一件落着。
付録 : 「scilab で遊ぼう」索引
- scilabで遊ぼう (1)
- scilabで遊ぼう (2)
- scilabで遊ぼう (3)
- scilabで遊ぼう (4)
- scilabで遊ぼう (5)
- scilabで遊ぼう (6)
- scilabで遊ぼう (7)
- scilabで遊ぼう (8)
- scilabで遊ぼう (9)
- scilabで遊ぼう (10)
- scilabで遊ぼう (11)
- scilabで遊ぼう (12)
- scilabで遊ぼう (13)
scilabで遊ぼう (6) [プログラム三昧]
今回から、実際の回路のシミュレーションをします。
scilabで、LCフィルタを作る
今回のシミュレーションの題材は、トランジスタ技術、2008年7月号と9月号に掲載されていた「dsPICで作るDC-DCコンバータのソフトウェア」という記事から採用しました。 まずは、出力直前に配置されているLCフィルタを作ります。
-->L1=39d-6; -->C1=680d-6; -->w0_lc=1.0/sqrt(L1*C1); -->Q=0.8; -->s=poly(0,'s'); -->lc_cont=syslin('c',w0_lc^2/(s^2+s*(w0_lc/Q)+w0_lc^2)) lc_cont = 37707391 ------------------------ 2 37707391 + 7675.793s + s -->scf(1);clf;bode(lc_cont,1,1e4);
LとCの値は決まっているのですが、Qの値は不明です。 そのため、適当に"0.8"としておきました。 ボード線図からわかるように、1000Hz付近にカットオフ周波数を持つ2次ローパス・フィルタの特性になっています。
このフィルタの出力は、20kHz(50µ秒周期)でサンプリングされるため、A/Dコンバータから得られる値の周波数特性は変化します。 このサンプリングに相当する操作を行うのが、"dscr"関数です。 ただし、"dscr"関数は、伝達関数ではなく状態空間モデルを扱います。 そのため、伝達関数と状態空間との間の変換を行う"tr2ss"関数と"ss2tr"関数を駆使します。
-->Ts=50d-6; -->lc_filter=ss2tf(dscr(tf2ss(lc_cont),Ts)) lc_filter = 0.0363518 + 0.0413237z -------------------------- 2 0.6812747 - 1.6035993z + z -->scf(1);clf;bode(lc_filter,1,1e4);
ナイキスト周波数である10kHzに近い箇所で特性が変化しています。
唯一の連続時間要素であるLCフィルタを離散時間表現に変換したので、この後は、離散時間での操作だけでシミュレーションすることができます。
2009年1月1日追記
あれ? 離散時間表現には、絶対時間の概念が無かったはずなのに、ボード線図は、絶対周波数で描かれています。 どうなっているんだ?
付録 : 「scilab で遊ぼう」索引
- scilabで遊ぼう (1)
- scilabで遊ぼう (2)
- scilabで遊ぼう (3)
- scilabで遊ぼう (4)
- scilabで遊ぼう (5)
- scilabで遊ぼう (6)
- scilabで遊ぼう (7)
- scilabで遊ぼう (8)
- scilabで遊ぼう (9)
- scilabで遊ぼう (10)
- scilabで遊ぼう (11)
- scilabで遊ぼう (12)
- scilabで遊ぼう (13)
参考文献
トランジスタ技術 (Transistor Gijutsu) 2008年 07月号 [雑誌]
- 作者:
- 出版社/メーカー: CQ出版
- 発売日: 2008/06/10
- メディア: 雑誌
トランジスタ技術 (Transistor Gijutsu) 2008年 09月号 [雑誌]
- 作者:
- 出版社/メーカー: CQ出版
- 発売日: 2008/08/09
- メディア: 雑誌
scilabで遊ぼう (5) [プログラム三昧]
今回は、ディジタル・フィルタにも使われる離散時間フィルタをつくります。
scilabは、zの式でもフィルタを記述できる
連続時間フィルタは、ラプラス変換後のsの式を使って記述しました。 同様に離散時間フィルタも、z変換後のzの式を使って記述することが出来ます。 フィルタを作成する関数は、またしても"syslin"です。
-->z=poly(0,'z'); -->sys=syslin('d',(1+z^(-1)+z^(-2)+z^(-3)+z^(-4))/5) sys = 2 3 4 0.2 + 0.2z + 0.2z + 0.2z + 0.2z ------------------------------- 4 z
ここで例題として作成したのは、5項の移動平均フィルタです。 連続時間フィルタの時と同様にボード線図を描かせることができます。
-->scf(1);clf;bode(sys,1e-2,1e2); WARNING: calfrq: Frequencies beyond Nyquist frequency are ignored.
100Hzまでのグラフを描かせるつもりだったのですが、「ナイキスト周波数を超えた周波数は無視します。」とメッセージが表示され、グラフは、0.5Hzで打ち切られてしまいました。
これを見ると、0.1Hz付近にカットオフ周波数のあるローパス・フィルタのような特性になっています。
scilabで、ステップ応答をみる
連続時間でのシミュレーションは、"csim"で行いましたが、離散時間でのシミュレーションは、"flts"を使います。
-->x=ones(1,101);
入力は、"1"ばかりで構成される101要素のベクトルです。
-->y=flts(x,sys);
この入力をフィルタ"sys"と共に"flts"に与えてシミュレーションを行います。
-->scf(1);clf;plot(t,x,t,y); -->ax1=gca();ax1.grid=[33,33];
このように、5秒後に100%に達するするどい立ち上がり特性が得られました。
scilabで、正弦波応答をみる
離散時間シミュレーションの場合も入力に正弦波を与えて応答をみることができます。
-->t=[0:1:100]; -->x=sin(0.05*2*%pi*t); -->y=flts(x,sys); -->scf(1);clf;plot(t,x,t,y); -->ax1=gca();ax1.grid=[33,33];
周波数が0.05Hzの時には、振幅は90%程度です。
-->x=sin(0.1*2*%pi*t); -->y=flts(x,sys); -->scf(1);clf;plot(t,x,t,y); -->ax1=gca();ax1.grid=[33,33];
一方、0.1Hzの時には、60%になっています。 これも広義のローパス・フィルタになっているようです。
離散時間フィルタにおける周波数の意味
ボード線図で周波数特性をみた時に判明したように、このフィルタはサンプリング周期が1秒であると想定されています。 もっと高い周波数に対応するディジタルフィルタは、どうやったらできるのでしょうか。 実は、離散時間フィルタだけを設計する時には、サンプリング時間の絶対値には意味がないのです。
ボード線図で表示されている「周波数」は、サンプリング周波数で「正規化」された相対周波数を示しているに過ぎません。 サンプリング周波数を千倍速くすると、周波数特性も単純に千倍高くなります。 しかしながら、別のアナログ・フィルタと組み合わせた時の周波数特性を考えたときには、周波数の絶対値を気にしなくてはなりませんので、どこかでサンプリング周波数の概念を入れなくてはなりません。 これは、次回以降の課題になります。
付録 : 「scilab で遊ぼう」索引
- scilabで遊ぼう (1)
- scilabで遊ぼう (2)
- scilabで遊ぼう (3)
- scilabで遊ぼう (4)
- scilabで遊ぼう (5)
- scilabで遊ぼう (6)
- scilabで遊ぼう (7)
- scilabで遊ぼう (8)
- scilabで遊ぼう (9)
- scilabで遊ぼう (10)
- scilabで遊ぼう (11)
- scilabで遊ぼう (12)
- scilabで遊ぼう (13)
scilabで遊ぼう (4) [プログラム三昧]
今回は、いよいよフィルタを作ってみます。
scilabは、sの関数でフィルタを記述する
最初は、アナログのローパス・フィルタをつくってみます。 アナログ・フィルタは、sの関数で記述します。 まず、変数sを定義します。
-->s=poly(0,'s');
次は、カットオフ周波数を指定します。 本来は、「ω0」としたいところですが、「w0」で代用しています。
-->w0=2*%pi;
最後は、フィルタの定義です。 変数sを使った式を書くとフィルタが出来上がります。 'c'は、「連続時間」のフィルタであることを示しています。
-->sys=syslin('c',1/(1+s/w0)) sys = 1 ------------- 1 + 0.1591549s
このフィルタの周波数特性をグラフ化するには、"bode"関数を使います。
-->scf(1);clf;bode(sys,1e-2,1e2);
1e-2と1e2は、表示する周波数の下限と上限です。 このコマンドで、ボード線図が表示されます。
カットオフ周波数(Magnitudeが-3dBになり、Phaseが-45°になる周波数)は、確かに1Hzになっています。
scilabで、ステップ応答をみる
出来上がったフィルタを使って、ステップ応答をみます。 ステップ応答のシミュレーションには、"csim"を使うと簡単です。
-->t=[0:0.02:2]; -->y=csim('step',t,sys); -->scf(1);clf;plot(t,y);
scilabで、正弦波応答をみる
つぎは、正弦波に対する応答をみます。 ここでも、"csim"を使います。
-->x=sin(2*%pi*t); -->y=csim(x,t,sys); -->scf(1);clf;plot(t,x,t,y);
入力に1Hzの正弦波を入れると、振幅は約0.7倍になります。
続いて、2Hzの正弦波を入れると、振幅は約0.5倍になります。 まさにローパス・フィルタです。
グラフにグリッドを表示したい
デフォルトの状態のグラフには、グリッドが表示されません。 これは、表示されていないのではなく背景と同じ色に設定されているので見えないということのようです。 グリッドを表示するためには、グリッドの色を変更します。
-->ax1=gca();ax1.grid=[33,33];
"gca"関数で、現在の図面の軸のコンテキストを獲得します。 そして、コンテキストに対して、x軸とy軸のそれぞれのグリッドの色を指定します。 "33"は、ボード線図のグリッドでも使用されている灰色です。
これで、振幅を確認しやすくなりました。
付録 : 「scilab で遊ぼう」索引
- scilabで遊ぼう (1)
- scilabで遊ぼう (2)
- scilabで遊ぼう (3)
- scilabで遊ぼう (4)
- scilabで遊ぼう (5)
- scilabで遊ぼう (6)
- scilabで遊ぼう (7)
- scilabで遊ぼう (8)
- scilabで遊ぼう (9)
- scilabで遊ぼう (10)
- scilabで遊ぼう (11)
- scilabで遊ぼう (12)
- scilabで遊ぼう (13)
scilabで遊ぼう (3) [プログラム三昧]
計算が出来たので、計算結果をグラフ表示してみます。
時間軸での表示
最初は、単純な正弦波を表示させます。 ここでは、1サイクルを20点に分割して4サイクル分(8π分)表示します。 そのため、0から80まで昇順に並ぶベクトル"t"を用意します。
-->t=[0:80] t = column 1 to 7 0. 1. 2. 3. 4. 5. 6. column 8 to 13 7. 8. 9. 10. 11. 12. column 14 to 19
全部で81項目もあるので、表示しきれず、一時停止してしまいます。 こんなときには、コマンドにセミコロンを追加して表示を省略することができます。
-->t=[0:80]; -->size(t) ans = 1. 81.
"size"関数でマトリックスの大きさを確認すると、1行81列であることがわかります。 つぎは、y軸に使うベクトルを用意します。
-->y=sin(2*%pi*t/20);
”%pi"は、円周率πを表現する定数です。 ベクトルを"sin"関数に渡すとベクトルの各要素の正弦を要素とするベクトルができます。
-->scf(1);clf; -->plot(t,y);
"scf"で図番号を指定し、"clf"で図をクリアします。 そして、"plot"でx軸とy軸を指定してグラフを表示します。
同様に余弦のベクトルもグラフ表示します。
-->x=cos(2*%pi*t/20); -->scf(1);clf; -->plot(t,y,t,x);
x軸とy軸の周期を調整するといわゆるリサージュ波形を表示させることができます。
-->t=[0:2100]; -->x=cos(2*%pi*t/700); -->y=sin(2*%pi*t/300); -->scf(1);clf; -->plot(x,y);
付録 : 「scilab で遊ぼう」索引
- scilabで遊ぼう (1)
- scilabで遊ぼう (2)
- scilabで遊ぼう (3)
- scilabで遊ぼう (4)
- scilabで遊ぼう (5)
- scilabで遊ぼう (6)
- scilabで遊ぼう (7)
- scilabで遊ぼう (8)
- scilabで遊ぼう (9)
- scilabで遊ぼう (10)
- scilabで遊ぼう (11)
- scilabで遊ぼう (12)
- scilabで遊ぼう (13)
scilabで遊ぼう (2) [プログラム三昧]
scilabは、多くの関数を備えた高機能関数電卓です。
scilabは、マトリックス演算ができる
前回、scilabは、マトリックス(行列)が扱えると書きました。 行列を扱うには、様々な操作が必要になってきますが、そういった操作は、関数で提供されます。
-->inv([1,2;3,4]) ans = - 2. 1. 1.5 - 0.5 -->inv([1,2;3,6]) !--error 19 Problem is singular.
"inv"は、逆行列を計算する関数です。 もちろん、正則な行列にしか適用できませんので、非正則な行列に対してはエラー・メッセージを返します。
-->det([1,2;3,4]) ans = - 2. -->det([1,2;3,6]) ans = 0.
行列が正則かどうかを確かめるためには、行列式が零になるかどうかを確認します。 "det"は、行列式を求める関数です。
-->[1,2;3,4]' ans = 1. 3. 2. 4. -->[1,2,3,4]' ans = 1. 2. 3. 4. -->[1,2,3,4]*[4,3,2,1]' ans = 20.
演算子「'」で、「転置行列」を計算します。 カンマでつないだベクトル(横行列)を転置すると、縦行列になります。 これを利用して、二つのベクトルの内積を計算させることができます。
付録 : 「scilab で遊ぼう」索引
- scilabで遊ぼう (1)
- scilabで遊ぼう (2)
- scilabで遊ぼう (3)
- scilabで遊ぼう (4)
- scilabで遊ぼう (5)
- scilabで遊ぼう (6)
- scilabで遊ぼう (7)
- scilabで遊ぼう (8)
- scilabで遊ぼう (9)
- scilabで遊ぼう (10)
- scilabで遊ぼう (11)
- scilabで遊ぼう (12)
- scilabで遊ぼう (13)
scilabで遊ぼう (1) [プログラム三昧]
マイコンでデジタル・フィルタをかじろうとしましたが、どうにも感じがつかめない。 何か良いシミュレータが無いか探したところ、scilabというソフトウェアを紹介してもらいました。
scilabは、電卓である
scilabは、Scilab Home Pageからダウンロードすることができます。 現在の最新バージョンは、5.0.3なのですが、頻繁に落ちてしまいます。 そのため、4.1.2を使用しています。
scilabは、私には電卓のように感じられました。 プログラムも出来るので、「プログラム関数電卓」といったところでしょうか。 こんな使い方をします。
-->1+2 ans = 3.
scilabは、ベクトルを扱える
scilabが、並みの電卓と違うところは、色々とあります。 その中でも、「ベクトルが扱える」という特徴が大きいと思います。 角かっこの中に数値をカンマで区切って並べるとベクトルを表現できます。
-->[1,2,3] ans = 1. 2. 3. -->[1,2,3]+2 ans = 3. 4. 5. -->[1,2,3]-2 ans = - 1. 0. 1. -->[1,2,3]*2 ans = 2. 4. 6. -->[1,2,3]/2 ans = 0.5 1. 1.5
ベクトルとスカラを演算子でつなぐとベクトルのそれぞれの要素との演算結果が得られます。
scilabは、マトリックスも扱える
ベクトルと同様にマトリックス(行列)も扱うことができます。 マトリックスの各行を分けるためには、セミコロンを使います。
-->[1,2;3,4] ans = 1. 2. 3. 4.
マトリックス同士の掛け算も簡単に行う事ができます。 10%誤差と20%誤差の二つの要素があったときの全体の誤差を計算すると、こうなります。
-->[0.9;1.0;1.1]*[0.8,1.0,1.2] ans = 0.72 0.9 1.08 0.8 1. 1.2 0.88 1.1 1.32
最小値が-28%で、最大値が+32%になります。
付録 : 「scilab で遊ぼう」索引
- scilabで遊ぼう (1)
- scilabで遊ぼう (2)
- scilabで遊ぼう (3)
- scilabで遊ぼう (4)
- scilabで遊ぼう (5)
- scilabで遊ぼう (6)
- scilabで遊ぼう (7)
- scilabで遊ぼう (8)
- scilabで遊ぼう (9)
- scilabで遊ぼう (10)
- scilabで遊ぼう (11)
- scilabで遊ぼう (12)
- scilabで遊ぼう (13)
Quartusで遊ぼう (11) [プログラム三昧]
先ごろ、ALTERAに請求していた"DVD"が届きました。 消印(?)を見たところ、スウェーデンから発送されたようです。 まさに、国境無しというところでしょうか。
すでに、"Quartus II"は、ダウンロードして、さんざん遊んだので、このDVDは「宝の持ち腐れ」になりそうな予感。 ちなみに、二枚のDVDは、Windows用とLinux用でした。
参考文献
トランジスタ技術 (Transistor Gijutsu) 2008年 12月号 [雑誌]
- 作者:
- 出版社/メーカー: CQ出版
- 発売日: 2008/11/10
- メディア: 雑誌
付録 : 「Quartus で遊ぼう」索引
- Quartus で遊ぼう (1)
- Altera の EPM2210F324 が話題になっているので、私も使ってみました。 ただし、ハードウェアは購入していないので、ソフトウェアで遊んだだけです。
- Quartus で遊ぼう (2)
- 「Quartus で遊ぼう」の二回目は、組み合わせ論理回路の合成を調べます。
- Quartus で遊ぼう (3)
- 論理合成後の状態を表示してくれるツールを探しました。
- Quartus で遊ぼう (4)
- 前回作成した4値のアップ・ダウンカウンタで論理合成後に使われているフリップ・フロップは、何個(何ビット)でしょうか?
- Quartus で遊ぼう (5)
- 今回は、ワン・ホット・コードを使ったステート・マシンでグリッチが発生すかどうかを観測します。
- Quartus で遊ぼう (6)
- トランジスタ技術誌に「リセット信号生成回路」のHDL記述がありました。 この記述は、ちょっともったいないですよ。
- Quartus で遊ぼう (7)
- トランジスタ技術誌に書かれていたインストラクション・デコーダは、3項演算子が連なっていました。 もっと、別の書き方はできないかな。
- Quartus で遊ぼう (8)
- Verilog の代入には、 <= と = の二種類が使われています。 これって、何が違うんでしょうかね。
- Quartus で遊ぼう (9)
- 2進数の足し算で、キャリーを取り出したい時、どうしましょうか。
- Quartus で遊ぼう (10)
- 作らせてみました。 リップル・キャリーカウンタ。
- Quartus で遊ぼう (11)
- 先ごろ、請求していた"DVD"が届きました。
Quartusで遊ぼう (10) [プログラム三昧]
作らせてみました。 リップル・キャリーカウンタ。
リップル・キャリーって何だ?
リップル・キャリー・カウンタは、あるフリップ・フロップの出力を次の段のフリップ・フロップのクロックとして使う回路です。 フリップ・フロップの出力の変化が次々とフリップ・フロップに伝達されるため、後段になるほど出力変化の時間が遅れていきます。 反面、半加算器が必要な同期式カウンタに比べると回路規模が圧倒的に小さくなるため、TTL時代のカウンタには良く使われていました。
リップル・キャリー型HDL記述
HDL 記述は、同期式カウンタに比べるとかなり複雑になってしいました。
module r_counter8(q, go, clk_b, reset_b); output[7:0] q; input go; input clk_b; input reset_b; reg[7:0] q; always @(negedge clk_b or negedge reset_b) begin if (!reset_b) q[0] <= 1'b0; else if (go) q[0] <= ~q[0]; end always @(negedge q[0] or negedge reset_b) begin if (!reset_b) q[1] <= 1'b0; else q[1] <= ~q[1]; end always @(negedge q[1] or negedge reset_b) begin if (!reset_b) q[2] <= 1'b0; else q[2] <= ~q[2]; end always @(negedge q[2] or negedge reset_b) begin if (!reset_b) q[3] <= 1'b0; else q[3] <= ~q[3]; end always @(negedge q[3] or negedge reset_b) begin if (!reset_b) q[4] <= 1'b0; else q[4] <= ~q[4]; end always @(negedge q[4] or negedge reset_b) begin if (!reset_b) q[5] <= 1'b0; else q[5] <= ~q[5]; end always @(negedge q[5] or negedge reset_b) begin if (!reset_b) q[6] <= 1'b0; else q[6] <= ~q[6]; end always @(negedge q[6] or negedge reset_b) begin if (!reset_b) q[7] <= 1'b0; else q[7] <= ~q[7]; end endmodule
"reset_b" は、非同期リセット入力です。 このカウンタは、 "go" がアサートされたときに限り、カウンタが進みます。 二段目以降のフリップ・フロップは、前段の出力が変化しない限り動作しないので、 "go" 条件を入れる必要はありません。
資源の消費量を調べた
論理合成をかけた結果、使用した Logic Element の個数は、8個となりました。 しかし、同期式カウンタの場合とはちょっと違っています。
Total registers | 8 / 2,210 ( < 1 % ) |
Total LABs | 4 / 221 ( 2 % ) |
Logic elements in carry chains | 0 |
この表は、 "Compilation Report → Fitter → Resource Section → Resource Usage Summary" から抜粋したものです。 確かに LE の数は、8個なのですが、 Logic Array Block (LAB) の数が4個になっています。 ひとつの LAB には、10個の LE が入っているので、40個の LE のうち20%しか使われていないことになります。
これは、ひとつの LAB には、二種類のクロックしか引き込めないという制約によるものです。 そのため、ひとつの LAB には二段分のフリップ・フロップしか存在できず、このような結果になりました。
残った未使用 LE は、どうなるかというと、おそらく、他のフリップ・フロップを使わない LE に割り当てられると思われますが、配線の制約は厳しくなると予想されます。
タイミングを調べた
次に "Compilation Report → Fitter → Timing Analyzer → Summary" でタイミング情報を調べてみました。
Type | Slack | Required Time | Actual Time | From | To | From Clock | To Clock | Failed Paths |
---|---|---|---|---|---|---|---|---|
Worst-case tsu | N/A | None | 0.390 ns | go | q[0]~reg0 | -- | clk_b | 0 |
Worst-case tco | N/A | None | 22.055 ns | q[7]~reg0 | q[7] | clk_b | -- | 0 |
Worst-case th | N/A | None | -0.044 ns | go | q[0]~reg0 | -- | clk_b | 0 |
Clock Setup: 'clk_b' | N/A | None | Restricted to 304.04 MHz ( period = 3.289 ns ) | q[2]~reg0 | q[2]~reg0 | clk_b | clk_b | 0 |
5行目の "Clock Setup" を見ると、このカウンタは300MHzのクロック入力に耐えることがわかります。 しかし、3行目の "Worst-case tco" を見ると、クロックが変化してからカウンタの最上位ビット q[7] が変化するまで最大22ナノ秒の時間が必要であることもわかります。 このため、カウンタの出力が信頼できるのはクロックが変化してから22ナノ秒後であり、カウンタの値に意味を持たせる場合には、40MHz程度の周波数でしか使用できません。 この "tco" (Time of Clock to Output) 時間は、カウンタの段数が増えるほど長くなるので、使用可能な周波数も低くなります。
同期式カウンタの場合
同じ仕様の8ビットの同期式カウンタを合成してみました。
reg[7:0] q; always @(negedge clk_b or negedge reset_b) begin if (!reset_b) q <= 8'd0; else q <= q + 8'd1; end
資源の消費量はこうなりました。
Total registers | 8 / 2,210 ( < 1 % ) |
Total LABs | 1 / 221 ( < 1 % ) |
Logic elements in carry chains | 7 |
同期式カウンタも8個の LE を使用しており、しかも一つの LAB で収まっています。 これは、 LE がカウンタや加算器を作りやすい構成になっているのためです。
タイミングは、こうなっています。
Type | Slack | Required Time | Actual Time | From | To | From Clock | To Clock | Failed Paths |
---|---|---|---|---|---|---|---|---|
Worst-case tco | N/A | None | 7.142 ns | q[5]~reg0 | q[5] | clk_b | -- | 0 |
Clock Setup: 'clk_b' | N/A | None | Restricted to 304.04 MHz ( period = 3.289 ns ) | q[0]~reg0 | q[7]~reg0 | clk_b | clk_b | 0 |
この場合も300MHzのクロック入力に耐えることが出来ますが、クロックが変化してから q[5] が変化するまで 7.1ナノ秒の時間が必要です。 このことから、カウンタの値に意味がある場合の最大周波数は140MHzということになります。
考察
回路規模が小さくなることを期待して、リップル・キャリー・カウンタを合成しましたが、積極的に使いたくなる性能は出ませんでした。 CPLD を使っている限りは、同期式カウンタを使ったほうが良いようです。
参考文献
トランジスタ技術 (Transistor Gijutsu) 2008年 12月号 [雑誌]
- 作者:
- 出版社/メーカー: CQ出版
- 発売日: 2008/11/10
- メディア: 雑誌
付録 : 「Quartus で遊ぼう」索引
- Quartus で遊ぼう (1)
- Altera の EPM2210F324 が話題になっているので、私も使ってみました。 ただし、ハードウェアは購入していないので、ソフトウェアで遊んだだけです。
- Quartus で遊ぼう (2)
- 「Quartus で遊ぼう」の二回目は、組み合わせ論理回路の合成を調べます。
- Quartus で遊ぼう (3)
- 論理合成後の状態を表示してくれるツールを探しました。
- Quartus で遊ぼう (4)
- 前回作成した4値のアップ・ダウンカウンタで論理合成後に使われているフリップ・フロップは、何個(何ビット)でしょうか?
- Quartus で遊ぼう (5)
- 今回は、ワン・ホット・コードを使ったステート・マシンでグリッチが発生すかどうかを観測します。
- Quartus で遊ぼう (6)
- トランジスタ技術誌に「リセット信号生成回路」のHDL記述がありました。 この記述は、ちょっともったいないですよ。
- Quartus で遊ぼう (7)
- トランジスタ技術誌に書かれていたインストラクション・デコーダは、3項演算子が連なっていました。 もっと、別の書き方はできないかな。
- Quartus で遊ぼう (8)
- Verilog の代入には、 <= と = の二種類が使われています。 これって、何が違うんでしょうかね。
- Quartus で遊ぼう (9)
- 2進数の足し算で、キャリーを取り出したい時、どうしましょうか。
- Quartus で遊ぼう (10)
- 作らせてみました。 リップル・キャリーカウンタ。
- Quartus で遊ぼう (11)
- 先ごろ、請求していた"DVD"が届きました。
Quartusで遊ぼう (9) [プログラム三昧]
2進数の足し算で、キャリーを取り出したい時、どうしましょうか。
トランジスタ技術誌の回答例
トランジスタ技術の記事で使用されていた例では、4bit + 4bit + 1bit の全加算器の出力を 5bit のワイヤに割り当てて、さらに sum 部と carry 部に分けていました。
assign work[4:0] = dat1[3:0] + dat2[3:0] + cin; assign cout = work[4]; assign out[3:0] = work[3:0];
もちろん、これでも答えは得られるのですが、 Verilog では、別の書き方が出来ます。
Verilog流の回答例
それは、 Perl にも似た文法です。
assign {cout,out[3:0]} = dat1[3:0] + dat2[3:0] + cin;
カーリー・ブラケット(中括弧?)は、複数のビット列を結合して束ねる機能なのですが、 Verilog では、右辺式ばかりでなく、左辺式にも使うことが出来ます。 このため、この一行だけで目的は達せられます。 ハーフ・キャリーつきの 8bit 加算も二行で書けます。
assign { dc,out[3:0]} = dat1[3:0] + dat2[3:0] + cin; assign {cout,out[7:4]} = dat1[7:4] + dat2[7:4] + dc;
合成してみたけれど
この回路は、単なる全加算器の回路なので、 Logic Element (LE) が8個使用されるだけと期待していたのですが、実際に合成してみると、12個の LE が使用されていました。
そこで、「テクノロジ・マップ」を表示させた結果がこれです。 これは、下位 4bit から上位 4bit にハーフキャリーを渡している箇所なのですが、なぜか、二つの LE がバッファとして使用されています。 これと同じバッファが最下位キャリーと最上位キャリーにも追加されているので、バッファに使用されている LE は合計4個もあります。 これに全加算器の分、8個を加えて12個という内訳です。
なぜ、バッファが挿入されたのかをオンライン・マニュアルで探しました。 まだ、はっきりとはしていませんが、キャリーが直接外部端子に出てくるときにバッファが挿入されているようです。 また、確認してみなきゃ。
参考文献
トランジスタ技術 (Transistor Gijutsu) 2008年 12月号 [雑誌]
- 作者:
- 出版社/メーカー: CQ出版
- 発売日: 2008/11/10
- メディア: 雑誌
付録 : 「Quartus で遊ぼう」索引
- Quartus で遊ぼう (1)
- Altera の EPM2210F324 が話題になっているので、私も使ってみました。 ただし、ハードウェアは購入していないので、ソフトウェアで遊んだだけです。
- Quartus で遊ぼう (2)
- 「Quartus で遊ぼう」の二回目は、組み合わせ論理回路の合成を調べます。
- Quartus で遊ぼう (3)
- 論理合成後の状態を表示してくれるツールを探しました。
- Quartus で遊ぼう (4)
- 前回作成した4値のアップ・ダウンカウンタで論理合成後に使われているフリップ・フロップは、何個(何ビット)でしょうか?
- Quartus で遊ぼう (5)
- 今回は、ワン・ホット・コードを使ったステート・マシンでグリッチが発生すかどうかを観測します。
- Quartus で遊ぼう (6)
- トランジスタ技術誌に「リセット信号生成回路」のHDL記述がありました。 この記述は、ちょっともったいないですよ。
- Quartus で遊ぼう (7)
- トランジスタ技術誌に書かれていたインストラクション・デコーダは、3項演算子が連なっていました。 もっと、別の書き方はできないかな。
- Quartus で遊ぼう (8)
- Verilog の代入には、 <= と = の二種類が使われています。 これって、何が違うんでしょうかね。
- Quartus で遊ぼう (9)
- 2進数の足し算で、キャリーを取り出したい時、どうしましょうか。
- Quartus で遊ぼう (10)
- 作らせてみました。 リップル・キャリーカウンタ。
- Quartus で遊ぼう (11)
- 先ごろ、請求していた"DVD"が届きました。
Quartusで遊ぼう (8) [プログラム三昧]
Verilog の代入には、 <= と = の二種類が使われています。 これって、何が違うんでしょうかね。
どっちが早い?
以下は、トランジスタ技術誌にあった mStack モジュールを一部書き直したものです。
always@(posedge clk or posedge reset) begin if(reset) begin stack1 <= 9'h0; stack2 <= 9'h0; end else if(Q) begin if (push) begin stack1 <= datain; stack2 <= stack1; end else if(pull) begin stack1 <= stack2; stack2 <= 9'h0; end end end
"push" がアサートされると、 datain の値が stack1 に代入され、さらに stack1 の値が stack2 に代入されています。 通常の C などの言語では、 stack1 にも stack2 にも datain の値が代入されますが、この例では、そうはなりません。
このノンブロッキング代入と呼ばれる代入文は、代入のきっかけとなる事象(この場合には "clk" の立ち上がり)以前の値を使って右辺を計算し、すべての右辺値の計算が終わってから実際の代入を行います。 そのため、この HDL で "push" がアサートされたときには、 datain の値が stack1 に代入され、それまで stack1 に入っていた値が stack2 に入るというシフト・レジスタ状の動作をします。
RTLレベルでシミュレーションを行うと、こうなりました。 "push" がアサートされているタイミングで、 stack1 と stack2 の値がそれぞれ変化する様子がわかります。
ブロッキング代入を使ったら
それでは、ノンブロッキング代入の代わりにブロッキング代入を使ったら、どうなるでしょうか。
: stack1 = 9'h0; stack2 = 9'h0; end else if(Q) begin if (push) begin stack1 = datain; stack2 = stack1; end else if(pull) begin stack1 = stack2; stack2 = 9'h0; :
RTL レベルでシミュレーションを行うと、こうなりました。 "push" がアサートされた時の動作を見ると、 datain の値が stack1 と stack2 に突き抜けているのがわかります。 実際の回路では、レーシングと呼ばれる現象です。
論理合成ではエラーが出ない
設計者にとっては、レーシングを起こす回路は誤りと認識されるのですが、機械にはわかりません。 そのため、論理合成時にはエラーが出ず、 stack1 と stack2 に datain の値が代入される HDL 記述どおりの回路ができてしまいます。
こればかりは、しっかりとテスト・パターンを書いて検証して気をつけるしかなさそうです。
参考文献
トランジスタ技術 (Transistor Gijutsu) 2008年 12月号 [雑誌]
- 作者:
- 出版社/メーカー: CQ出版
- 発売日: 2008/11/10
- メディア: 雑誌
付録 : 「Quartus で遊ぼう」索引
- Quartus で遊ぼう (1)
- Altera の EPM2210F324 が話題になっているので、私も使ってみました。 ただし、ハードウェアは購入していないので、ソフトウェアで遊んだだけです。
- Quartus で遊ぼう (2)
- 「Quartus で遊ぼう」の二回目は、組み合わせ論理回路の合成を調べます。
- Quartus で遊ぼう (3)
- 論理合成後の状態を表示してくれるツールを探しました。
- Quartus で遊ぼう (4)
- 前回作成した4値のアップ・ダウンカウンタで論理合成後に使われているフリップ・フロップは、何個(何ビット)でしょうか?
- Quartus で遊ぼう (5)
- 今回は、ワン・ホット・コードを使ったステート・マシンでグリッチが発生すかどうかを観測します。
- Quartus で遊ぼう (6)
- トランジスタ技術誌に「リセット信号生成回路」のHDL記述がありました。 この記述は、ちょっともったいないですよ。
- Quartus で遊ぼう (7)
- トランジスタ技術誌に書かれていたインストラクション・デコーダは、3項演算子が連なっていました。 もっと、別の書き方はできないかな。
- Quartus で遊ぼう (8)
- Verilog の代入には、 <= と = の二種類が使われています。 これって、何が違うんでしょうかね。
- Quartus で遊ぼう (9)
- 2進数の足し算で、キャリーを取り出したい時、どうしましょうか。
- Quartus で遊ぼう (10)
- 作らせてみました。 リップル・キャリーカウンタ。
- Quartus で遊ぼう (11)
- 先ごろ、請求していた"DVD"が届きました。
Quartusで遊ぼう (7) [プログラム三昧]
トランジスタ技術誌に書かれていたインストラクション・デコーダは、3項演算子が連なっていました。 もっと、別の書き方はできないかな。
オリジナル版の合成結果
CQ出版のWEBページからダウンロード版を取り寄せて、 "mCmdDecode" モジュールだけ合成しました。 このモジュールは、純粋な組み合わせ回路なので、クロック周波数を与えても制約になりません。 そこで、 "Settings" ダイアログの "Timing Analysis Settings → Classic Timing Analyzer Settings" のページにある、 "tpd" (Time of Propagation Delay : 伝達遅延時間)に 0ナノ秒の時間を指定しました。 これで、「出来るだけがんばれ」と解釈してくれるはずです。
その結果、 LE の消費量は70個、最大伝達遅延時間は10.304ナノ秒になりました。 けっこう、でかいね。
casez文を使ってみた
3項演算子を連ねた場合、式を左から解釈していくので原則として順序が発生してしまいます。 つまり、そのままでは、プライオリティ・エンコーダが構成されてしまうかもしれません。
これに対して、case文は、重複の無い複数の値と同時に比較して、分岐を行います。 そのため、case文に書かれた選択肢には順序がありません。 しかし、case文は比較対象のビット幅が固定なので、ドント・ケア (Don't care) を指示する事はできません。
そこで登場するのが、casez文です。 casez文では、符号 Z (高インピーダンス状態)を使ってドント・ケアを指定することができます。 符号 Z は、 ? とも書き表すことができます。 これを利用して、インストラクション・デコーダを構成します。
reg[4:0] inst; always @(cmd) begin casez (cmd) 12'b0001_11??_????: inst = `ADDWF; 12'b0001_01??_????: inst = `ANDWF; 12'b0000_011?_????: inst = `CLRF; 12'b0000_0100_0000: inst = `CLRW; 12'b0010_01??_????: inst = `COMF; 12'b0000_11??_????: inst = `DECF; 12'b0010_11??_????: inst = `DECFSZ; 12'b0010_10??_????: inst = `INCF; 12'b0011_11??_????: inst = `INCFSZ; 12'b0001_00??_????: inst = `IORWF; 12'b0010_00??_????: inst = `MOVF; 12'b0000_001?_????: inst = `MOVWF; 12'b0011_01??_????: inst = `RLF; 12'b0011_00??_????: inst = `RRF; 12'b0000_10??_????: inst = `SUBWF; 12'b0011_10??_????: inst = `SWAPF; 12'b0001_10??_????: inst = `XORWF; 12'b0100_????_????: inst = `BCF; 12'b0101_????_????: inst = `BSF; 12'b0110_????_????: inst = `BTFSC; 12'b0111_????_????: inst = `BTFSS; 12'b1110_????_????: inst = `ANDLW; 12'b1001_????_????: inst = `CALL; 12'b101?_????_????: inst = `GOTO; 12'b1101_????_????: inst = `IORLW; 12'b1100_????_????: inst = `MOVLW; 12'b1000_????_????: inst = `RETLW; 12'b1111_????_????: inst = `XORLW; 12'b0000_0000_0010: inst = `OPTION; 12'b0000_0000_0100: inst = `CLRWDT; 12'b0000_0000_011?: inst = `TRIS; default: inst = `NOP; endcase end
この記述を使って、同じ条件で合成を行うと、 LE の消費量は58個、最大伝達遅延時間は9.201ナノ秒になりました。 ずいぶん、かわったね。
inst 出力の値を指定するために、 "inst" というレジスタを宣言しています。 このレジスタは、HDL記述で便宜的に宣言されたレジスタなので、最終的な合成後のネットリストには、現れません。 "Compilation Report → Analysis & Synthesis → Resource Usage Summary" にも58個の LE が全て組み合わせロジックに使われてと書かれています。
Total logic elements 58 -- Combinational with no register 58 -- Register only 0 -- Combinational with a register 0
ただし、always 構文のセンシティビティ・リスト (Sensitivity list) の項目が足りなかったり、レジスタが設定されない条件が書かれたりした場合には、思ったとおりの論理が生成されません。 その場合、エラーが発生すれば良いのですが、黙って妙な論理を作ってしまう可能性もあるので、注意が必要です。
参考文献
- Quartus II Help Version 8.1
-
always 構文で組み合わせ回路を構成する際の条件は、 Quartus II のオンライン・マニュアルの "Creating Designes → Using AHDL, VHDL & Verilog HDL → Using Verilog HDL in the Quartus II Software → Implementing Combinational Logic → Using Always Constructs" に記述があります。
トランジスタ技術 (Transistor Gijutsu) 2008年 12月号 [雑誌]
- 作者:
- 出版社/メーカー: CQ出版
- 発売日: 2008/11/10
- メディア: 雑誌
付録 : 「Quartus で遊ぼう」索引
- Quartus で遊ぼう (1)
- Altera の EPM2210F324 が話題になっているので、私も使ってみました。 ただし、ハードウェアは購入していないので、ソフトウェアで遊んだだけです。
- Quartus で遊ぼう (2)
- 「Quartus で遊ぼう」の二回目は、組み合わせ論理回路の合成を調べます。
- Quartus で遊ぼう (3)
- 論理合成後の状態を表示してくれるツールを探しました。
- Quartus で遊ぼう (4)
- 前回作成した4値のアップ・ダウンカウンタで論理合成後に使われているフリップ・フロップは、何個(何ビット)でしょうか?
- Quartus で遊ぼう (5)
- 今回は、ワン・ホット・コードを使ったステート・マシンでグリッチが発生すかどうかを観測します。
- Quartus で遊ぼう (6)
- トランジスタ技術誌に「リセット信号生成回路」のHDL記述がありました。 この記述は、ちょっともったいないですよ。
- Quartus で遊ぼう (7)
- トランジスタ技術誌に書かれていたインストラクション・デコーダは、3項演算子が連なっていました。 もっと、別の書き方はできないかな。
- Quartus で遊ぼう (8)
- Verilog の代入には、 <= と = の二種類が使われています。 これって、何が違うんでしょうかね。
- Quartus で遊ぼう (9)
- 2進数の足し算で、キャリーを取り出したい時、どうしましょうか。
- Quartus で遊ぼう (10)
- 作らせてみました。 リップル・キャリーカウンタ。
- Quartus で遊ぼう (11)
- 先ごろ、請求していた"DVD"が届きました。
Quartusで遊ぼう (6) [プログラム三昧]
トランジスタ技術誌に「リセット信号生成回路」のHDL記述がありました。 CPLDリセット後、およそ100ミリ秒の間、アサートされる信号です。 この記述は、ちょっともったいないですよ。
オリジナル版の合成
まずは、トランジスタ技術誌上のHDL記述を試してみます。
module mReset(reset, Q, clk); output reset; input Q; input clk; wire count_en; // Enable count up reg[15:0] reset_count; // assertion timer. assign count_en = (reset_count!=16'h8000)?(1'b1):(1'b0); assign reset = ~reset_count[15]; always @(posedge clk) begin if (Q && count_en) begin reset_count = reset_count + 16'd1; end end endmodule
信号線とレジスタの名前は、自分流に変更してあります。 これを論理合成すると使用 Logic Element (LE) は、22個となりました。 16ビットのカウンタのほかに6個の LE が使用されています。 その使い道をテクノロジ・マップで見てみました。
すると、6個の LE がカウンタ値の一致検出回路に消費されていることがわかりました。 16ビットのカウンタの値を比較しているので、これだけ大量の LE が必要なのです。
一致比較を大小比較に変更してみる
HDL 記述では、16ビットの値の一致検出を行っていました。 このリセット信号生成モジュールでは、リセット出力がネゲートされたらカウンタを停止させています。 そのため、無理に「一致検出」をする必要は無く、「大小比較」でも十分に同じ機能を得られるはずです。 そこで、一致検出部分を大小比較に変更してみました。
: assign count_en = (reset_count>=16'h8000)?(1'b1):(1'b0); :
その結果、使用した LE の数は17個に減少し、回路もこんなに簡単になりました。 こんなもんで、いいでしょ。
厳密に100ミリ秒が欲しい
トランジスタ技術誌で作成されているプロセッサは、タイミング信号 Q をアサートしています。 このタイミング信号は、4MHzの内部クロックをおよそ1/50した周波数80kHzの信号です。 そのため、 Q のパルスを数えて、100ミリ秒の時間を稼ぐには、8000クロックを数える必要があります。 では、ちょうど8000パルスでリセットがアサートされるように変更しましょう。
: reg[12:0] reset_count; // assertion timer. assign reset = (reset_count >= 13'd8000); assign count_en = !reset; always @(posedge clk) begin if (Q && count_en) begin reset_count = reset_count + 13'd1; end end :
カウンタのビット数が16から13に減ったので、必要な LE の数も減っただろうと思ったら、所用 LE 数は、17のままでした。 理由は、簡単です。 十進数の 8000 は、2進数では 1111101000000 になります。 そのため、大小比較のために6ビットの比較を行わなくてはならなかったというわけです。 ここは、カウンタのビット数を14ビットに増やして、14'h2000 (十進数で8192)と比較してやると、 LE の数が減らせます。
: reg[13:0] reset_count; // assertion timer. assign reset = (reset_count >= 14'h2000); assign count_en = !reset; always @(posedge clk) begin if (Q && count_en) begin reset_count = reset_count + 14'd1; end end :
カウンタのビット数が増えたのに、 LE の数は、15個に減少しました。 リセットの時間が、2.4%増えますが、このぐらいの誤差なら許容してもらえるでしょう。
参考文献
トランジスタ技術 (Transistor Gijutsu) 2008年 12月号 [雑誌]
- 作者:
- 出版社/メーカー: CQ出版
- 発売日: 2008/11/10
- メディア: 雑誌
付録 : 「Quartus で遊ぼう」索引
- Quartus で遊ぼう (1)
- Altera の EPM2210F324 が話題になっているので、私も使ってみました。 ただし、ハードウェアは購入していないので、ソフトウェアで遊んだだけです。
- Quartus で遊ぼう (2)
- 「Quartus で遊ぼう」の二回目は、組み合わせ論理回路の合成を調べます。
- Quartus で遊ぼう (3)
- 論理合成後の状態を表示してくれるツールを探しました。
- Quartus で遊ぼう (4)
- 前回作成した4値のアップ・ダウンカウンタで論理合成後に使われているフリップ・フロップは、何個(何ビット)でしょうか?
- Quartus で遊ぼう (5)
- 今回は、ワン・ホット・コードを使ったステート・マシンでグリッチが発生すかどうかを観測します。
- Quartus で遊ぼう (6)
- トランジスタ技術誌に「リセット信号生成回路」のHDL記述がありました。 この記述は、ちょっともったいないですよ。
- Quartus で遊ぼう (7)
- トランジスタ技術誌に書かれていたインストラクション・デコーダは、3項演算子が連なっていました。 もっと、別の書き方はできないかな。
- Quartus で遊ぼう (8)
- Verilog の代入には、 <= と = の二種類が使われています。 これって、何が違うんでしょうかね。
- Quartus で遊ぼう (9)
- 2進数の足し算で、キャリーを取り出したい時、どうしましょうか。
- Quartus で遊ぼう (10)
- 作らせてみました。 リップル・キャリーカウンタ。
- Quartus で遊ぼう (11)
- 先ごろ、請求していた"DVD"が届きました。
Quartusで遊ぼう (5) [プログラム三昧]
前回の「Quartusで遊ぼう (4)」でステート・マシンがワン・ホット・コードで構成されたことが確認されました。 今回は、ワン・ホット・コードを使ったステート・マシンでグリッチが発生すかどうかを観測します。
RTLシミュレーションでは、グリッチは発生しない
今回題材として使うのは、デューティ50%出力の4分周カウンタです。
module onehot(q, clk, reset_b); output q; input clk; input reset_b; parameter ST_0 = 4'b0000; parameter ST_1 = 4'b0011; parameter ST_2 = 4'b0101; parameter ST_3 = 4'b1001; reg q; // synthesis syn_encoding = "user" reg[3:0] st; always @(posedge clk or negedge reset_b) begin if (!reset_b) begin st <= ST_0; end else case(st) ST_0: st <= ST_1; ST_1: st <= ST_2; ST_2: st <= ST_3; ST_3: st <= ST_0; endcase end always @(st) begin case (st) ST_2, ST_3: q = 1'b1; default: q = 1'b0; endcase end endmodule
実験用の HDL なので、内容にあまり意味はありません。 テストベンチは、これです。
`timescale 1ns / 1ps module tb_onehot(); wire q; reg clk; reg reset_b; parameter CLK_PERIOD = 20; onehot onehot_1( .q (q), .clk (clk), .reset_b (reset_b) ); initial begin clk = 0; forever begin #(CLK_PERIOD/2) clk = ~clk; end end initial begin reset_b = 0; repeat (10) @(posedge clk); #5 reset_b = 1; repeat (50) @(posedge clk); $stop; end endmodule
RTLによるシミュレーションを行うと、一行目のクロックに同期して2から5行目のフリップ・フロップの出力が変化します。 そのため、6行目の q 出力にグリッチが発生するはずがありません。
ゲート・レベル・シミュレーションの実行手順
トランジスタ技術誌でも、シミュレーションの方法が紹介されていましたが、RTLによる理想的な状態でのシミュレーションでした。 そのため、50GHzというむちゃくちゃなクロックを与えても回路が動作しているように見えました。 ここでは、合成後の状態でのシミュレーションの方法を説明します。
ゲート・レベル・ネットリストを作成する
まず、 Quartus II でゲート・レベル・ネットリストと遅延情報ファイルを作成します。 これらを作成するには、オプションの設定が必要です。 メニューの "Assignments → EDA Tool Settings" から "Settings" ダイアログを開きます。 次に "Category" から "EDA Tool Settings → Simulation" を選択します。 すると、このようなダイアログが開くので、 "Tool name" に "ModelSim-Altera" を、 "Format for output netlist" (出力するネットリストのフォーマット)に "Verilog" を、 "Time scale" (シミュレーションの単位時間)に "1ps" を指定します。
オプションを設定したら、メニューの "Processing → Start Compilation" でネットリストを作成します。 ここで生成されたファイルは、 "Compilation Report → EDA Netlist Writer → Simulation → Generated Files" から確認できます。 ここでは、 "onehot.vo" というゲート・レベル・ネットリストと "onehot_v.sdo" という遅延情報ファイルが作成されています。
シミュレーションを実行する
ネットリストが揃ったので、シミュレーションを行います。 "ModelSim - Altera" を起動したら、メニューから "File → New → Project..." を選び "Create Project" ダイアログを開きます。 "Project Name" に適当なプロジェクト名を指定し、 "Project Location" には、 Quartus II で作成したプロジェクト・ディレクトリの下にある "simulation/modelsim" を指定します。 このディレクトリは、 Quartus II が作成したゲート・レベル・ネットリストが入るデフォルトのディレクトリです。
すると、 "Add items to the Project" というダイアログが開くので、 "Add Existing File" をクリックし、シミュレーションに使用するネットリストを選択します。 ここで指定するファイルは、 "onehot.vo" と "tb_onehot.v" の二つです。
メニューから "Compile → Compile All" を選んでこれら二つのファイルをコンパイルします。 コンパイルすると、シミュレーションに使用可能なファイルが生成されます。
シミュレーションを実行するには、メニューから "Simulate → Start Simulation..." を選択します。 すると、 "Start Simulation" ダイアログが開きます。 このダイアログで設定すべき箇所は二つです。
一つ目は、テストベンチのトップレベルの指定です。 これは、 "Design" タブで "work → tb_onehot" を選びます。
二つ目は、 CPLD の素子などが収められたライブラリの指定です。 これは、 "Libraries" タブで "Search Libraries" にある "Add..." ボタンをクリックして、 "maxii_ver" を選択します。 これで、 "MAX II" 用に "verilog" で記述されたコンパイル済みライブラリを参照することができます。
以上で設定準備は完了です。 ダイアログの "OK" ボタンをクリックすると、シミュレーションが実行されます。
ゲート・レベル・シミュレーションの場合
カウンタをゲートレベルシミュレーションにかけてみました。 伝達遅延情報が入っているので、1行目のクロックが立ち上がってから4行目の q 出力が立ち下がるまで 6.005ナノ秒の時間を要しているのがわかります。 この場合にも、グリッチは観測されません。 理由は、すべてのフリップ・フロップの clk から出力への伝達遅延が全く等しいからです。
シミュレーションでは、観測されませんが、危険な構成であることに間違いは無いと思います。 シミュレーションでは検出できないのかな?
クロック周波数を上げてみた
ゲート・レベル・ネットリストを使用したシミュレーションで、クロック周波数を上げてみました。 これは、テスト・ベンチのパラメータ "CLK_PERIOD" を変更するだけで手軽に試すことが出来ます。 その結果、 "CLK_PERIOD" を 4 まで減らす、つまり250MHzで動作させるとフリップ・フロップが正しく動作しなくなり、出力が出なくなることが確認されました。 やっぱり、50GHzでは、動作しないっしょ。
参考サイト
- Quartus II Development Software Literature
-
この記事は、このWEBページに並んでいるマニュアルの "Volume 3: Verification → Section I. Simulation → Chapter 2. Mentor Graphics ModelSim Support" を参照して書きました。 ただし、メニューの構成が実物のシミュレータと相違している箇所がいくつもあるので、注意が必要です。
参考文献
トランジスタ技術 (Transistor Gijutsu) 2008年 12月号 [雑誌]
- 作者:
- 出版社/メーカー: CQ出版
- 発売日: 2008/11/10
- メディア: 雑誌
付録 : 「Quartus で遊ぼう」索引
- Quartus で遊ぼう (1)
- Altera の EPM2210F324 が話題になっているので、私も使ってみました。 ただし、ハードウェアは購入していないので、ソフトウェアで遊んだだけです。
- Quartus で遊ぼう (2)
- 「Quartus で遊ぼう」の二回目は、組み合わせ論理回路の合成を調べます。
- Quartus で遊ぼう (3)
- 論理合成後の状態を表示してくれるツールを探しました。
- Quartus で遊ぼう (4)
- 前回作成した4値のアップ・ダウンカウンタで論理合成後に使われているフリップ・フロップは、何個(何ビット)でしょうか?
- Quartus で遊ぼう (5)
- 今回は、ワン・ホット・コードを使ったステート・マシンでグリッチが発生すかどうかを観測します。
- Quartus で遊ぼう (6)
- トランジスタ技術誌に「リセット信号生成回路」のHDL記述がありました。 この記述は、ちょっともったいないですよ。
- Quartus で遊ぼう (7)
- トランジスタ技術誌に書かれていたインストラクション・デコーダは、3項演算子が連なっていました。 もっと、別の書き方はできないかな。
- Quartus で遊ぼう (8)
- Verilog の代入には、 <= と = の二種類が使われています。 これって、何が違うんでしょうかね。
- Quartus で遊ぼう (9)
- 2進数の足し算で、キャリーを取り出したい時、どうしましょうか。
- Quartus で遊ぼう (10)
- 作らせてみました。 リップル・キャリーカウンタ。
- Quartus で遊ぼう (11)
- 先ごろ、請求していた"DVD"が届きました。
Quartusで遊ぼう (4) [プログラム三昧]
前回の「Quartusで遊ぼう (3)」で4値のアップ・ダウンカウンタを作成しました。 さて、このカウンタで論理合成後に使われているフリップ・フロップは、何個(何ビット)でしょうか?
ワン・ホット・コード
図のテクノロジ・マップを見てわかるように、正解は4個です。 HDL では、2ビットのレジスタに4種類のコードをキレイに割り当てて、ジョンソン・カウンタを作らせようとしていました。 ところが、出来上がったのは、ワン・ホット・コードのカウンタ。 ワン・ホット・コードとは、ステート・マシンの個々の状態に対して一つのフリップ・フロップを割り当てる手法です。 このため、同時には単一のフリップ・フロップ出力がホットになる(アサートされる)ので、この名前で呼ばれています。
ワン・ホット・コードの利点は、状態コードをデコードする論理が不要なので、デコード回路が縮小できることです。 また、欠点は、フリップ・フロップの数が増えることです。 CPLD の場合には、デコード回路とフリップ・フロップの比率がはじめから決まっているので、双方を天秤にかけることになると思います。
ジョンソン・カウンタにしておくれ
ワン・ホット・コードなんか使っていられるか。 という向きには、 "syn_encoding" という属性を付けて、別のコーディングを使うこともできます。 以下の例では、ジョンソン・カウンタで構成させています。
: reg q; // synthesis syn_encoding = "johnson" reg[1:0] st; always @(posedge clk or negedge reset_b) begin if (!reset_b) begin :
出来上がった状態遷移表がこれです。 もうひとつ、納得できない割り当てだなあ。
ST_0 0 0 ST_1 1 0 ST_3 1 1 ST_2 0 1
設定選択肢には、 "gray" (グレイ・コード)や "compact" (最小ビット数)なんてのもありますが、4状態の場合にはどれもジョンソンと同じになってしまうので、ここでは省略。 論理合成ツールが信用できない場合には、 "user" を指定すると、 HDL に記述したとおりのコードを割り当ててくれます。
論理合成時の設定を変えることもできる
逐一、論理合成の方法を指定していられないという場合には、すべての論理合成で使用される設定を変更することも出来ます。
まず、 Task ペインの "Compile Design → Analysis & Synthesis → Edit Settings" から "Analysis & Synthesis Settings" ダイアログを開きます。 さらに、そのダイアログの "More Settings..." ボタンで "More Analysis & Synthesis Settings" ダイアログを』開きます。 このダイアログで "State Machine Processing" を変更します。 深すぎだよ。
初期設定は、 "Auto" となっています。 マニュアルによれば、 "Auto" を設定すると、「FPGA の場合にはワン・ホット・コードを使い、 CPLD の場合には最小ビット数を使う」と書いてあります。 あれ? 何で、ワン・ホット・コードを使ってくれたんだろう?
ワン・ホット・コードの問題点
ワン・ホット・コードは、 FPGA などに適用すると、論理が簡単になるという利点はあるのですが、状態遷移のときに必ず二つのフリップ・フロップが状態を変えます。 そのため、ステート・マシンの状態をデコードして出力を得るような場合には、状態遷移時にグリッチが発生する可能性があります。 また、他の設定でもヘタなコード割り当てをすると同じ問題が起こります。 次回は、シミュレータでグリッチが発生する様子が観測出来るか試してみます。
参考サイト
- Quartus II Development Software Literature
-
ステート・マシンの選択肢に関する説明は、"Volume 1: Design and Synthesis → Section III. Synthesis → Chapter 8. Quartus II Integrated Synthesis" の "State Machine Processing" (page 8-35) と "Manually Specifying State Assignments Using the syn_encoding Attribute" (page 8-36) に記述があります。
参考文献
トランジスタ技術 (Transistor Gijutsu) 2008年 12月号 [雑誌]
- 作者:
- 出版社/メーカー: CQ出版
- 発売日: 2008/11/10
- メディア: 雑誌
付録 : 「Quartus で遊ぼう」索引
- Quartus で遊ぼう (1)
- Altera の EPM2210F324 が話題になっているので、私も使ってみました。 ただし、ハードウェアは購入していないので、ソフトウェアで遊んだだけです。
- Quartus で遊ぼう (2)
- 「Quartus で遊ぼう」の二回目は、組み合わせ論理回路の合成を調べます。
- Quartus で遊ぼう (3)
- 論理合成後の状態を表示してくれるツールを探しました。
- Quartus で遊ぼう (4)
- 前回作成した4値のアップ・ダウンカウンタで論理合成後に使われているフリップ・フロップは、何個(何ビット)でしょうか?
- Quartus で遊ぼう (5)
- 今回は、ワン・ホット・コードを使ったステート・マシンでグリッチが発生すかどうかを観測します。
- Quartus で遊ぼう (6)
- トランジスタ技術誌に「リセット信号生成回路」のHDL記述がありました。 この記述は、ちょっともったいないですよ。
- Quartus で遊ぼう (7)
- トランジスタ技術誌に書かれていたインストラクション・デコーダは、3項演算子が連なっていました。 もっと、別の書き方はできないかな。
- Quartus で遊ぼう (8)
- Verilog の代入には、 <= と = の二種類が使われています。 これって、何が違うんでしょうかね。
- Quartus で遊ぼう (9)
- 2進数の足し算で、キャリーを取り出したい時、どうしましょうか。
- Quartus で遊ぼう (10)
- 作らせてみました。 リップル・キャリーカウンタ。
- Quartus で遊ぼう (11)
- 先ごろ、請求していた"DVD"が届きました。
Quartusで遊ぼう (3) [プログラム三昧]
前回の記事「Quartusで遊ぼう (2)」では、 1bit × 12word 構成の ROM を合成させましたが、どんな風に合成されたのかわかりませんでした。 きっと、調べる方法があるに違いないと考えて、マニュアルを紐解きました
ネットリスト・ビューワ
マニュアルを探した結果、「ネットリスト・ビューワ (Netlist Viewer) 」というものが存在することがわかりました。 名前の通りですと、単にテキストのネットリストが表示されるだけのような印象がありましたが、実は、そうではなかったようです。 ネットリスト・ビューワには、三つの種類があり、いずれのビューワも Task ペインの "Compile Design → Analysis & Synthesis → Netlist Viewers" から開くことができます。
RTLビューワ
ひとつ目は、「RTLビューワ (RTL Viewer) 」と呼ばれるもので、Verilogのソース・コードを解析した結果を表示してくれます。 「Quartus で遊ぼう (1)」で作成した pikapika.v を表示させると、このようにグラフィカルに表示されます。
ステート・マシン・ビューワ
二つ目は、「ステート・マシン・ビューワ (State Machine Viewer) 」です。 HDL 記述中からステート・マシンを抜き出して、状態遷移図を表示してくれます。 ただし、 Quartus II が HDL 記述のをステート・マシンとして認識するための条件があるので、その条件を満たさないとステート・マシン・ビューワは働いてくれません。 この図を出したときに使ったステート・マシンは、以下の4値アップ・ダウン・カウンタです。
module udcount(q, dn, clk, reset_b); output q; input dn; input clk; input reset_b; parameter ST_0 = 2'b00; parameter ST_1 = 2'b01; parameter ST_2 = 2'b11; parameter ST_3 = 2'b10; reg q; reg[1:0] st; always @(posedge clk or negedge reset_b) begin if (!reset_b) begin st <= ST_0; end else begin case (st) ST_0: st <= (dn)?(ST_0):(ST_1); ST_1: st <= (dn)?(ST_0):(ST_2); ST_2: st <= (dn)?(ST_1):(ST_3); ST_3: st <= (dn)?(ST_2):(ST_3); endcase end end always @(st) begin case (st) ST_2: q = 1'b1; default: q = 1'b0; endcase end endmodule
テクノロジ・マップ・ビューワ
最後の「テクノロジ・マップ・ビューワ (Technology Map Viewer) 」は、合成後の状態を図示するビューワです。 この例は、「Quartusで遊ぼう (2)」で合成した 1bit × 128word 構成の ROM を示す図にキャプションを付けたものです。 左側の8個の Logic Element (LE) が、16ビットの ROM 8面分に相当し、右側の5個の LE が ROM からの8本の出力信号のマルチプレクサになっています。 これで、 1bit × 128word 構成の ROM が最大13個の LE で構成可能であることが確認できました。
参考サイト
- Quartus II Development Software Literature
-
この記事は、このWEBページに並んでいるマニュアルの "Volume 1: Design and Synthesis → Section III. Synthesis → Chapter 12. Analyzing Designs with Quartus II Netlist Viewers" を参照して書きました。
また、ステート・マシンの認識条件は、 "Volume 1: Design and Synthesis → Section II. Design Guidelines → Chapter 6. Recommended HDL Coding Styles" の "State Machines" (page 6-51) に書いてあります。
Quartus II のマニュアルは、全部で 37MByte の巨大な PDF 文書だそうです。 そんなに巨大なファイルを Acrobat reader で開いて、大丈夫なのかなあ。
参考文献
トランジスタ技術 (Transistor Gijutsu) 2008年 12月号 [雑誌]
- 作者:
- 出版社/メーカー: CQ出版
- 発売日: 2008/11/10
- メディア: 雑誌
付録 : 「Quartus で遊ぼう」索引
- Quartus で遊ぼう (1)
- Altera の EPM2210F324 が話題になっているので、私も使ってみました。 ただし、ハードウェアは購入していないので、ソフトウェアで遊んだだけです。
- Quartus で遊ぼう (2)
- 「Quartus で遊ぼう」の二回目は、組み合わせ論理回路の合成を調べます。
- Quartus で遊ぼう (3)
- 論理合成後の状態を表示してくれるツールを探しました。
- Quartus で遊ぼう (4)
- 前回作成した4値のアップ・ダウンカウンタで論理合成後に使われているフリップ・フロップは、何個(何ビット)でしょうか?
- Quartus で遊ぼう (5)
- 今回は、ワン・ホット・コードを使ったステート・マシンでグリッチが発生すかどうかを観測します。
- Quartus で遊ぼう (6)
- トランジスタ技術誌に「リセット信号生成回路」のHDL記述がありました。 この記述は、ちょっともったいないですよ。
- Quartus で遊ぼう (7)
- トランジスタ技術誌に書かれていたインストラクション・デコーダは、3項演算子が連なっていました。 もっと、別の書き方はできないかな。
- Quartus で遊ぼう (8)
- Verilog の代入には、 <= と = の二種類が使われています。 これって、何が違うんでしょうかね。
- Quartus で遊ぼう (9)
- 2進数の足し算で、キャリーを取り出したい時、どうしましょうか。
- Quartus で遊ぼう (10)
- 作らせてみました。 リップル・キャリーカウンタ。
- Quartus で遊ぼう (11)
- 先ごろ、請求していた"DVD"が届きました。
Quartusで遊ぼう (2) [プログラム三昧]
「Quartus で遊ぼう」の二回目は、組み合わせ論理回路の合成を調べます。
組み合わせ論理回路とは、何ぞや
論理合成で回路を作成する場合、全ての回路は二つの種類に分けることが出来ます。 ひとつが組み合わせ論理 (combinational logic) で、もうひとつが非組み合わせ論理 (non-combinational logic) です。
非組み合わせ論理は、フリップ・フロップのようにクロックに従って動作する回路を示します。 原則としてクロックが来なければ出力が変化することがありません。
一方、組み合わせ論理は、入力の変化がすぐに出力に影響するものをいいます。 AND-OR-NOT を組み合わせてつくった回路は、組み合わせ論理になるはずです。
もちろん、 AND-OR-NOT を駆使してフリップ・フロップをつくることも出来ます。 出来ますが、安全な回路を論理合成させる場合にはやってはいけない事になっています。
ROM は、組み合わせ論理回路である
ROM (Read Only Memory) とは、アドレス入力に従って、一意にデータ出力を行う回路です。 そのため、紛れもなく、組み合わせ論理回路であるといえます。 逆に、どんな組み合わせ論理回路も ROM で表現することができます。 ここでは、 ROM を合成させて、出来上がりの状態を調べてみます。
1bit × 16word の場合
最初は、 1bit × 16word 構成の ROM を合成してみます。
module rom1x16(data, addr); output data; input[3:0] addr; // Pseudo register reg data; // ROM codes always @(addr) begin case (addr[3:0]) 4'b0000: data = 1'b0; 4'b0001: data = 1'b1; 4'b0010: data = 1'b1; 4'b0011: data = 1'b0; 4'b0100: data = 1'b1; 4'b0101: data = 1'b0; 4'b0110: data = 1'b0; 4'b0111: data = 1'b1; 4'b1000: data = 1'b1; 4'b1001: data = 1'b0; 4'b1010: data = 1'b0; 4'b1011: data = 1'b1; 4'b1100: data = 1'b0; 4'b1101: data = 1'b1; 4'b1110: data = 1'b1; 4'b1111: data = 1'b0; endcase end endmodule
論理圧縮が出来ないようにパターンを作成してみました。 この論理を合成させると、以下のようなサマリが表示されました。
Flow Status Successful - Tue Nov 18 18:57:28 2008 Quartus II Version 8.1 Build 163 10/28/2008 SJ Web Edition Revision Name rom1x16 Top-level Entity Name rom1x16 Family MAX II Device EPM2210F324C3 Timing Models Final Met timing requirements Yes Total logic elements 1 / 2,210 ( < 1 % ) Total pins 5 / 272 ( 2 % ) Total virtual pins 0 UFM blocks 0 / 1 ( 0 % )
Logic Element は、2210個のうちの1個しか使われていません。 MAX II のデータシートによると、 Logic Element (LE) には、4入力1出力の LUT (Look-Up Table) が内蔵されているため、このぐらいの容量であれば、一個の LE で構成できます。
8bit × 16word の場合
次は、出力ビット幅を変えてみます。 8ビットの出力を出させてみました。
module rom8x16(data, addr); output[7:0] data; input[3:0] addr; // Pseudo register reg[7:0] data; // ROM codes always @(addr) begin case (addr[3:0]) 4'b0000: data = 8'h01; 4'b0001: data = 8'h02; 4'b0010: data = 8'h04; 4'b0011: data = 8'h08; 4'b0100: data = 8'h10; 4'b0101: data = 8'h20; 4'b0110: data = 8'h40; 4'b0111: data = 8'h80; 4'b1000: data = 8'h40; 4'b1001: data = 8'h20; 4'b1010: data = 8'h10; 4'b1011: data = 8'h08; 4'b1100: data = 8'h04; 4'b1101: data = 8'h02; 4'b1110: data = 8'h01; 4'b1111: data = 8'h00; endcase end endmodule
これを合成させると、このようになりました。
Total logic elements 8 / 2,210 ( < 1 % ) Total pins 12 / 272 ( 4 % ) Total virtual pins 0 UFM blocks 0 / 1 ( 0 % )
単純に Logic Element の数がビット幅分になっただけのようです。 以降の実験では、1ビット出力のものだけ検証していきます。
1bit × 32word の場合
次は、アドレス入力を一本増やして、 1bit × 32word 構成の ROM を合成してみます。
module rom1x32(data, addr); output data; input[4:0] addr; // Pseudo register reg data; // ROM codes always @(addr) begin case (addr[4:0]) 5'b00000: data = 1'b0; 5'b00001: data = 1'b1; 5'b00010: data = 1'b1; 5'b00011: data = 1'b0; 5'b00100: data = 1'b1; 5'b00101: data = 1'b0; 5'b00110: data = 1'b0; 5'b00111: data = 1'b1; 5'b01000: data = 1'b1; 5'b01001: data = 1'b0; 5'b01010: data = 1'b0; 5'b01011: data = 1'b1; 5'b01100: data = 1'b0; 5'b01101: data = 1'b1; 5'b01110: data = 1'b1; 5'b01111: data = 1'b0; 5'b10000: data = 1'b1; 5'b10001: data = 1'b0; 5'b10010: data = 1'b0; 5'b10011: data = 1'b1; 5'b10100: data = 1'b0; 5'b10101: data = 1'b1; 5'b10110: data = 1'b1; 5'b10111: data = 1'b0; 5'b11000: data = 1'b0; 5'b11001: data = 1'b1; 5'b11010: data = 1'b1; 5'b11011: data = 1'b1; 5'b11100: data = 1'b1; 5'b11101: data = 1'b0; 5'b11110: data = 1'b0; 5'b11111: data = 1'b1; endcase end endmoduleサマリは、このようになりました。
Total logic elements 3 / 2,210 ( < 1 % ) Total pins 6 / 272 ( 2 % ) Total virtual pins 0 UFM blocks 0 / 1 ( 0 % )
Logic Element の数は、3個に増えました。 これは、例えば、 addr[4] が 0 の場合の出力を得るのに一個、 addr[4] が 1 の場合の出力を得るのに一個、これらの出力を束ねるために一個、使われたものと推測されます。
1bit × 128word の場合
最後は、アドレス入力を二本増やして、 1bit × 128word 構成の ROM を合成してみます。
module rom1x128(data, addr); output data; input[6:0] addr; // Pseudo register reg data; // ROM codes always @(addr) begin case (addr[6:0]) 7'h00:data=1'b0; 7'h01:data=1'b1; 7'h02:data=1'b1; 7'h03:data=1'b0; 7'h04:data=1'b1; 7'h05:data=1'b0; 7'h06:data=1'b0; 7'h07:data=1'b1; 7'h08:data=1'b1; 7'h09:data=1'b0; 7'h0A:data=1'b0; 7'h0B:data=1'b1; 7'h0C:data=1'b0; 7'h0D:data=1'b1; 7'h0E:data=1'b1; 7'h0F:data=1'b0; 7'h10:data=1'b0; 7'h11:data=1'b0; 7'h12:data=1'b0; 7'h13:data=1'b1; 7'h14:data=1'b0; 7'h15:data=1'b1; 7'h16:data=1'b1; 7'h17:data=1'b0; 7'h18:data=1'b0; 7'h19:data=1'b1; 7'h1A:data=1'b0; 7'h1B:data=1'b0; 7'h1C:data=1'b1; 7'h1D:data=1'b1; 7'h1E:data=1'b0; 7'h1F:data=1'b1; 7'h20:data=1'b1; 7'h21:data=1'b0; 7'h22:data=1'b0; 7'h23:data=1'b1; 7'h24:data=1'b1; 7'h25:data=1'b1; 7'h26:data=1'b1; 7'h27:data=1'b0; 7'h28:data=1'b0; 7'h29:data=1'b0; 7'h2A:data=1'b1; 7'h2B:data=1'b0; 7'h2C:data=1'b1; 7'h2D:data=1'b0; 7'h2E:data=1'b1; 7'h2F:data=1'b1; 7'h30:data=1'b1; 7'h31:data=1'b1; 7'h32:data=1'b1; 7'h33:data=1'b0; 7'h34:data=1'b1; 7'h35:data=1'b1; 7'h36:data=1'b0; 7'h37:data=1'b1; 7'h38:data=1'b1; 7'h39:data=1'b0; 7'h3A:data=1'b1; 7'h3B:data=1'b1; 7'h3C:data=1'b0; 7'h3D:data=1'b1; 7'h3E:data=1'b1; 7'h3F:data=1'b1; 7'h40:data=1'b1; 7'h41:data=1'b0; 7'h42:data=1'b1; 7'h43:data=1'b1; 7'h44:data=1'b0; 7'h45:data=1'b0; 7'h46:data=1'b1; 7'h47:data=1'b0; 7'h48:data=1'b1; 7'h49:data=1'b1; 7'h4A:data=1'b1; 7'h4B:data=1'b0; 7'h4C:data=1'b1; 7'h4D:data=1'b0; 7'h4E:data=1'b0; 7'h4F:data=1'b1; 7'h50:data=1'b0; 7'h51:data=1'b1; 7'h52:data=1'b1; 7'h53:data=1'b1; 7'h54:data=1'b0; 7'h55:data=1'b0; 7'h56:data=1'b0; 7'h57:data=1'b1; 7'h58:data=1'b1; 7'h59:data=1'b1; 7'h5A:data=1'b0; 7'h5B:data=1'b1; 7'h5C:data=1'b0; 7'h5D:data=1'b1; 7'h5E:data=1'b0; 7'h5F:data=1'b0; 7'h60:data=1'b0; 7'h61:data=1'b1; 7'h62:data=1'b0; 7'h63:data=1'b0; 7'h64:data=1'b1; 7'h65:data=1'b0; 7'h66:data=1'b0; 7'h67:data=1'b0; 7'h68:data=1'b0; 7'h69:data=1'b0; 7'h6A:data=1'b0; 7'h6B:data=1'b1; 7'h6C:data=1'b0; 7'h6D:data=1'b0; 7'h6E:data=1'b1; 7'h6F:data=1'b0; 7'h70:data=1'b1; 7'h71:data=1'b1; 7'h72:data=1'b0; 7'h73:data=1'b1; 7'h74:data=1'b0; 7'h75:data=1'b1; 7'h76:data=1'b0; 7'h77:data=1'b0; 7'h78:data=1'b0; 7'h79:data=1'b1; 7'h7A:data=1'b1; 7'h7B:data=1'b0; 7'h7C:data=1'b0; 7'h7D:data=1'b0; 7'h7E:data=1'b0; 7'h7F:data=1'b1; endcase end endmodule
サマリは以下のようになりました。
Total logic elements 13 / 2,210 ( < 1 % ) Total pins 8 / 272 ( 3 % ) Total virtual pins 0 UFM blocks 0 / 1 ( 0 % )
論理圧縮をさせないように、パターンを工夫した結果、 LE の数を13個まで増やすことが出来ましたが、これが上限であるかどうかは、定かではありません。 128ビットのROMを構成するのに最低限必要な8個の LE の出力を、5個の LE で束ねたものと推測されます。 実際に、どういう方法で束ねたのかは、わかっていません。
考察
以上の実験から、ROMを構成した場合に必要となる Logic Element の数は、ROMの総ビット数 ÷ 8 程度であると考えられます。 実際には、論理圧縮などされて、さらに少ない数の LE が使用されると思われます。
参考文献
トランジスタ技術 (Transistor Gijutsu) 2008年 12月号 [雑誌]
- 作者:
- 出版社/メーカー: CQ出版
- 発売日: 2008/11/10
- メディア: 雑誌
付録 : 「Quartus で遊ぼう」索引
- Quartus で遊ぼう (1)
- Altera の EPM2210F324 が話題になっているので、私も使ってみました。 ただし、ハードウェアは購入していないので、ソフトウェアで遊んだだけです。
- Quartus で遊ぼう (2)
- 「Quartus で遊ぼう」の二回目は、組み合わせ論理回路の合成を調べます。
- Quartus で遊ぼう (3)
- 論理合成後の状態を表示してくれるツールを探しました。
- Quartus で遊ぼう (4)
- 前回作成した4値のアップ・ダウンカウンタで論理合成後に使われているフリップ・フロップは、何個(何ビット)でしょうか?
- Quartus で遊ぼう (5)
- 今回は、ワン・ホット・コードを使ったステート・マシンでグリッチが発生すかどうかを観測します。
- Quartus で遊ぼう (6)
- トランジスタ技術誌に「リセット信号生成回路」のHDL記述がありました。 この記述は、ちょっともったいないですよ。
- Quartus で遊ぼう (7)
- トランジスタ技術誌に書かれていたインストラクション・デコーダは、3項演算子が連なっていました。 もっと、別の書き方はできないかな。
- Quartus で遊ぼう (8)
- Verilog の代入には、 <= と = の二種類が使われています。 これって、何が違うんでしょうかね。
- Quartus で遊ぼう (9)
- 2進数の足し算で、キャリーを取り出したい時、どうしましょうか。
- Quartus で遊ぼう (10)
- 作らせてみました。 リップル・キャリーカウンタ。
- Quartus で遊ぼう (11)
- 先ごろ、請求していた"DVD"が届きました。
Quartus で遊ぼう (1) [プログラム三昧]
Altera の EPM2210F324 が ここやここで話題になっているので、私も使ってみました。 ただし、ハードウェアは購入していないので、ソフトウェアで遊んだだけです。
DVDが待ちきれなかった
ダウンロードすべきファイルが、1Gバイトを超えるあまりにも巨大なものだったので、DVDを請求しました。 ところが、ちょうどソフトウェアのバージョンアップと重なったために、DVDの発送は、11月17日以降になるらしいことが書いてありました。 そのため、苦労して、ここからソフトウェアをダウンロードしてきました。
ダウンロードの方法には、二つあります。 一つは、ブラウザからHTTPで取り寄せる方法。 もう一つは、"Download Manager"という魅惑のダウンロードソフトを使うことです。 できたら、未知のソフトは使いたくなかったので、最初はHTTPを使ってダウンロードを試みました。 ところが、プロバイダか何かが制限をかけているらしく、600Mバイトを超えたところから急にバンド幅を制限されてしまい、終了まで十何時間かかるという表示が出てきてしまいます。 しかたなく、"Download Manager"を使うことにしました。 このソフトウェア、サーバに対して複数のコネクションを張り、あるコネクションにバンド幅制限をかけられたら、別のコネクションを張りなおしてダウンロードを続けているように見えます。 これじゃあ、プロバイダのバンド幅制限も効かないわけです。
インストールは、長いだけ
本日、インストールしたのは、"Quartus II 8.1"と"ModelSim - Altera"の二つです。 インストールは、時間が長くかかるだけで、特に難しいところはないと思います。 一箇所だけ、引っかかったのは、"ModelSim - Altera"のインストールの終盤で出てきた、このダイアログです。
ライセンスを許可するためのハードウェア・セキュリティ・キーが、PCのパラレル・ポートかUSBポートに接続されていますか?
もし、他の機器からライセンスを取り寄せたり、ライセンス許可にMACアドレスを使う場合には、"No"を選択しなくてはなりません。 ハードウェア・セキュリティ・キーを入手する場合には、ソフトウェア・ドライバが必要です。
"ModelSim"で使われるハードウェア・キーを使う場合には、"Yes"を選んでドライバをインストールします。 ハードウェア・キーに対応する最新のドライバがすでにインストールされているのであれば、"No"を選びます。 もし、ドライバが最新かそうでないのか判断できない場合には、"Yes"を選ぶことを推奨します。
"Yes"を選んだ場合には、"ModelSim"をインストールした後、PCを再起動する必要があります。
今から使おうとしているのは、ライセンス不要のはずの"ModelSim - Altera"なので、「ライセンスが不要の場合は」という条件を探したのですが、そういう選択肢はないようです。 お勧めに従って、"Yes"でドライバをインストールし、あとで再起動することにしました。
HDLを書いてみた
再起動した後、"Quartus"を起動し、よくわからないまま、プロジェクトを作成し、Verilogファイルを書きました。 Verilogファイルの作成には、"New"メニュー・ダイアログの"Design Files → Verilog HDL File"を選択してエディタを開きます。
module pikapika ( led, clk, reset_b ); output[7:0] led; input clk; input reset_b; reg[7:0] led; reg[12:0] ctr; always @(posedge clk or negedge reset_b) begin if (!reset_b) begin ctr <= 0; end else begin ctr <= ctr + 1; end end wire ctr_en = (ctr == 0); always @(posedge clk or negedge reset_b) begin if (!reset_b) begin led <= 8'd0; end else if(ctr_en) begin if (led == 8'd0) begin led <= 8'd1; end else begin led <= led << 1; end end end endmodule
作成したファイルは、SimさんのHDLをまねて、LEDが流れるようにしたものです。 文法は、古い Verilog に従っています。 左側の"Task"ペインの"Compile &Design"をダブル・クリックすると、一連のコンパイルが始まりました。 コンパイルの結果が、このような表示になって表れました。
2210個の LAB (Logic Array Block) のうち、25個を使っていることがわかります。 ほぼ、レジスタ(reg素子)の数と等しくなっています。 ここから、各入出力を具体的なピンに割り当てなくてはならないんですよね。
シミュレーションをしてみた
コンパイルが終わったら、シミュレータが走り始めました。 中身だけでは、動作するわけがないので、テストベンチを作りました。
`timescale 1ns/10ps module tb_pikapika(); // Clock period parameter CLK_PERIOD = 20; reg clk; reg reset_b; wire[7:0] led; pikapika pika1( .led(led), .clk(clk), .reset_b(reset_b) ); // Clock generator initial begin clk = 0; forever begin #(CLK_PERIOD/2); clk = ~clk; end end // Reset generator initial begin reset_b = 0; repeat (10) @(posedge clk); reset_b = 1; repeat(100000) begin @(posedge clk); end $stop; end endmodule
トランジスタ技術の記事では、"`timescalse 1ps/1ps"となっているのですが、このままでは、50GHzの超高速クロックで動作してしまいます。 "CLK_PERIOD"がナノ秒単位である事を示すためには、"`timescale 1ns/10ps"とする必要があります。 分解能を短くしすぎるとシミュレーションに時間がかかるので、せいぜい100倍ぐらいにします。 この程度なら、"`timescale 1ns/1ns"でも十分ですね。
シミュレーションの結果がこれです。 LEDの点滅周期は、
20n × 9 × 213 = 1.47456m [秒]
と、計算どおりです。 ただ、これでは、人間の目には点滅が見えません。 かといって、カウンタの分周比を大きくするとシミュレーションが終わらなくなるので、このあたりで妥協しておきます。
次の課題
トランジスタ技術によると、「マスクROMを構成させると、1ビットあたり1個のLABが必要になる」と書いてありました。 本当にLAB一個で1ビットの出力しか出てこないのであれば、ちょっとしたデコーダを作るだけで大量のLABを消費します。 このへんの真偽を確かめてみようと思います。
参考文献
トランジスタ技術 (Transistor Gijutsu) 2008年 12月号 [雑誌]
- 作者:
- 出版社/メーカー: CQ出版
- 発売日: 2008/11/10
- メディア: 雑誌
付録 : 「Quartus で遊ぼう」索引
- Quartus で遊ぼう (1)
- Altera の EPM2210F324 が話題になっているので、私も使ってみました。 ただし、ハードウェアは購入していないので、ソフトウェアで遊んだだけです。
- Quartus で遊ぼう (2)
- 「Quartus で遊ぼう」の二回目は、組み合わせ論理回路の合成を調べます。
- Quartus で遊ぼう (3)
- 論理合成後の状態を表示してくれるツールを探しました。
- Quartus で遊ぼう (4)
- 前回作成した4値のアップ・ダウンカウンタで論理合成後に使われているフリップ・フロップは、何個(何ビット)でしょうか?
- Quartus で遊ぼう (5)
- 今回は、ワン・ホット・コードを使ったステート・マシンでグリッチが発生すかどうかを観測します。
- Quartus で遊ぼう (6)
- トランジスタ技術誌に「リセット信号生成回路」のHDL記述がありました。 この記述は、ちょっともったいないですよ。
- Quartus で遊ぼう (7)
- トランジスタ技術誌に書かれていたインストラクション・デコーダは、3項演算子が連なっていました。 もっと、別の書き方はできないかな。
- Quartus で遊ぼう (8)
- Verilog の代入には、 <= と = の二種類が使われています。 これって、何が違うんでしょうかね。
- Quartus で遊ぼう (9)
- 2進数の足し算で、キャリーを取り出したい時、どうしましょうか。
- Quartus で遊ぼう (10)
- 作らせてみました。 リップル・キャリーカウンタ。
- Quartus で遊ぼう (11)
- 先ごろ、請求していた"DVD"が届きました。
DISCARD(廃棄)サーバ [プログラム三昧]
古くなって捨てられていたコンピュータを再生した話ではありません。
私のDISCARD(破棄)サーバ
某所での実験を追試しようとしましたが、通信相手のDISCARD(破棄)サーバというのを作成しなくてはならないことがわかりました。 どうも、データをどんどん受信してしまい、自分からは何も出さないサーバのことらしいです。 ちょいと、作ってしまおう。
/* * -------------------------------------------- * File : TcpDiscardServer.java * Package : org.noritan.tcpdiscard * Copyright : Copyright (c) 2008 noritan.org * Organization : noritan.org * Created : 2008/09/28 * -------------------------------------------- */ package org.noritan.tcpdiscard; import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; /** * ThisTcpDiscardServer
class is a so-called DISCARD * server. All received data will be discarded. * * @author noritan */ public class TcpDiscardServer implements Runnable { private int port; /** * Construct aTcpDiscardServer
object with * a specified port numer. * * @param pot * Port number to wait for a requests. */ public TcpDiscardServer(int port) { this.port = port; } /** * Server process content. * Served as a single thread DISCARD server. * * @see java.lang.Runnable#run() */ @Override public void run() { try { ServerSocket server = new ServerSocket(port); try { for (;;) { Socket socket = server.accept(); System.out.println( "Connected by " + socket.getInetAddress() ); System.out.println("SEND BUFFER="+socket.getSendBufferSize()); try { discard(socket.getInputStream()); } catch (IOException ex) { System.out.println("Failed to get streams"); } finally { try { socket.close(); } catch (IOException ex) { // do nothing for close } System.out.println("Socket closed"); } } } catch (IOException ex) { System.out.println("Failed to accept a socket"); ex.printStackTrace(); } finally { try { server.close(); } catch (IOException ex) { // do nothing for close. } } } catch (IOException ex) { System.out.println("Failed to open a server"); ex.printStackTrace(); } } /** * Discards all data at theinputStream
* * @param inputStream * InputStream arrives data to be discarded. */ private void discard(InputStream inputStream) { long count = 0; try { while (inputStream.read() >= 0) { count++; } } catch (IOException ex) { System.out.println("Error on InputStream"); ex.printStackTrace(); } System.out.println("RECEIVED=" + count); } /** * A main method as an application. * * @param args * Command line option not in use. */ public static void main(String[] args) { new Thread(new TcpDiscardServer(30049)).start(); } }
横着をしているので、クライアント一台にしか対応していません。 通信の終了時には、総受信バイト数だけを表示します。
Python で算数の穴埋め問題を解く [プログラム三昧]
今日は、プログラミング・ネタです。 数年前に子供が持って帰った算数の宿題にこんな問題がありました。
人間が解くときには、理詰めで枝を刈りながら解くのですが、 計算機にやらせるときには、全ての組み合わせについて力任せに解きます。 ちょうど、カブレていた Python でプログラミングしてみました。
# $Id: homework.py,v 1.2 2006/12/03 09:14:59 noritan Exp $ nums = [2,3,4,6,8] def mult(ans): return (ans[0]+ans[1]*0.1+ans[2]*0.01)*(ans[3]+ans[4]*0.1) def permutation(n, m): if m < 1: yield () else: for base in permutation(n, m-1): for x in xrange(n): if x not in base: yield base + (x,) def expression(ans): return '%1d.%1d%1d X %1d.%1d' % tuple(ans) minvalue = (9999, None) maxvalue = (-9999, None) length = len(nums) for idx in permutation(length, 5): ans = [nums[i] for i in idx] value = mult(ans) if value > maxvalue[0]: maxvalue = (value, ans) if value < minvalue[0]: minvalue = (value, ans) print minvalue[0], expression(minvalue[1]) print maxvalue[0], expression(maxvalue[1])
permutationで、全ての組み合わせを列挙させ、 multで掛け算の結果を求めて、 最小のものと最大のものをminvalueとmaxvalueに 格納していきます。 実行結果は、あっというまに出てきます。
8.832 3.68 X 2.4 53.286 6.42 X 8.3
最小になるのは、 3.68 × 2.4 = 8.832 で、最大になるのは、 6.42 × 8.3 = 53.286 と なりました。
参考文献
図書館で借りて読みました。
こっちは、買ってしまいました。