So-net無料ブログ作成
プログラム三昧 ブログトップ
- | 次の20件

scilabで遊ぼう (6) [プログラム三昧]このエントリーを含むはてなブックマーク#

WS000199.png

今回から、実際の回路のシミュレーションをします。

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);
 
WS000218.png

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);
 
WS000219.png

ナイキスト周波数である10kHzに近い箇所で特性が変化しています。

唯一の連続時間要素であるLCフィルタを離散時間表現に変換したので、この後は、離散時間での操作だけでシミュレーションすることができます。

2009年1月1日追記

あれ? 離散時間表現には、絶対時間の概念が無かったはずなのに、ボード線図は、絶対周波数で描かれています。 どうなっているんだ?

付録 : 「scilab で遊ぼう」索引

参考文献

トランジスタ技術 (Transistor Gijutsu) 2008年 07月号 [雑誌]

トランジスタ技術 (Transistor Gijutsu) 2008年 07月号 [雑誌]

  • 作者:
  • 出版社/メーカー: CQ出版
  • 発売日: 2008/06/10
  • メディア: 雑誌
トランジスタ技術 (Transistor Gijutsu) 2008年 09月号 [雑誌]

トランジスタ技術 (Transistor Gijutsu) 2008年 09月号 [雑誌]

  • 作者:
  • 出版社/メーカー: CQ出版
  • 発売日: 2008/08/09
  • メディア: 雑誌

scilabで遊ぼう (5) [プログラム三昧]このエントリーを含むはてなブックマーク#

WS000199.png

今回は、ディジタル・フィルタにも使われる離散時間フィルタをつくります。

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で打ち切られてしまいました。

WS000212.png

これを見ると、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];
WS000213.png

このように、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];
WS000216.png

周波数が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];
WS000215.png

一方、0.1Hzの時には、60%になっています。 これも広義のローパス・フィルタになっているようです。

離散時間フィルタにおける周波数の意味

ボード線図で周波数特性をみた時に判明したように、このフィルタはサンプリング周期が1秒であると想定されています。 もっと高い周波数に対応するディジタルフィルタは、どうやったらできるのでしょうか。 実は、離散時間フィルタだけを設計する時には、サンプリング時間の絶対値には意味がないのです。

ボード線図で表示されている「周波数」は、サンプリング周波数で「正規化」された相対周波数を示しているに過ぎません。 サンプリング周波数を千倍速くすると、周波数特性も単純に千倍高くなります。 しかしながら、別のアナログ・フィルタと組み合わせた時の周波数特性を考えたときには、周波数の絶対値を気にしなくてはなりませんので、どこかでサンプリング周波数の概念を入れなくてはなりません。 これは、次回以降の課題になります。

付録 : 「scilab で遊ぼう」索引


scilabで遊ぼう (4) [プログラム三昧]このエントリーを含むはてなブックマーク#

WS000199.png

今回は、いよいよフィルタを作ってみます。

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は、表示する周波数の下限と上限です。 このコマンドで、ボード線図が表示されます。

WS000207.png

カットオフ周波数(Magnitudeが-3dBになり、Phaseが-45°になる周波数)は、確かに1Hzになっています。

scilabで、ステップ応答をみる

出来上がったフィルタを使って、ステップ応答をみます。 ステップ応答のシミュレーションには、"csim"を使うと簡単です。

-->t=[0:0.02:2];
 
-->y=csim('step',t,sys);
 
-->scf(1);clf;plot(t,y);
WS000208.png

scilabで、正弦波応答をみる

つぎは、正弦波に対する応答をみます。 ここでも、"csim"を使います。

-->x=sin(2*%pi*t);
 
-->y=csim(x,t,sys);
 
-->scf(1);clf;plot(t,x,t,y);

入力に1Hzの正弦波を入れると、振幅は約0.7倍になります。

WS000209.png

続いて、2Hzの正弦波を入れると、振幅は約0.5倍になります。 まさにローパス・フィルタです。

WS000210.png

グラフにグリッドを表示したい

デフォルトの状態のグラフには、グリッドが表示されません。 これは、表示されていないのではなく背景と同じ色に設定されているので見えないということのようです。 グリッドを表示するためには、グリッドの色を変更します。

-->ax1=gca();ax1.grid=[33,33];

"gca"関数で、現在の図面の軸のコンテキストを獲得します。 そして、コンテキストに対して、x軸とy軸のそれぞれのグリッドの色を指定します。 "33"は、ボード線図のグリッドでも使用されている灰色です。

WS000211.png

これで、振幅を確認しやすくなりました。

付録 : 「scilab で遊ぼう」索引


scilabで遊ぼう (3) [プログラム三昧]このエントリーを含むはてなブックマーク#

WS000199.png

計算が出来たので、計算結果をグラフ表示してみます。

時間軸での表示

最初は、単純な正弦波を表示させます。 ここでは、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軸を指定してグラフを表示します。

WS000203.png

同様に余弦のベクトルもグラフ表示します。

-->x=cos(2*%pi*t/20);
 
-->scf(1);clf;
 
-->plot(t,y,t,x);

WS000204.png

x軸とy軸の周期を調整するといわゆるリサージュ波形を表示させることができます。

-->t=[0:2100];
 
-->x=cos(2*%pi*t/700);
 
-->y=sin(2*%pi*t/300);
 
-->scf(1);clf;
 
-->plot(x,y);

WS000206.png

付録 : 「scilab で遊ぼう」索引


scilabで遊ぼう (2) [プログラム三昧]このエントリーを含むはてなブックマーク#

WS000199.png

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) [プログラム三昧]このエントリーを含むはてなブックマーク#

WS000199.png

マイコンでデジタル・フィルタをかじろうとしましたが、どうにも感じがつかめない。 何か良いシミュレータが無いか探したところ、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 で遊ぼう」索引


Quartusで遊ぼう (11) [プログラム三昧]このエントリーを含むはてなブックマーク#

2434525

先ごろ、ALTERAに請求していた"DVD"が届きました。 消印(?)を見たところ、スウェーデンから発送されたようです。 まさに、国境無しというところでしょうか。

すでに、"Quartus II"は、ダウンロードして、さんざん遊んだので、このDVDは「宝の持ち腐れ」になりそうな予感。 ちなみに、二枚のDVDは、Windows用とLinux用でした。

参考文献

トランジスタ技術 (Transistor Gijutsu) 2008年 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で遊ぼう (10) [プログラム三昧]このエントリーを含むはてなブックマーク#

WS000219-320.png

作らせてみました。 リップル・キャリーカウンタ。

リップル・キャリーって何だ?

リップル・キャリー・カウンタは、あるフリップ・フロップの出力を次の段のフリップ・フロップのクロックとして使う回路です。 フリップ・フロップの出力の変化が次々とフリップ・フロップに伝達されるため、後段になるほど出力変化の時間が遅れていきます。 反面、半加算器が必要な同期式カウンタに比べると回路規模が圧倒的に小さくなるため、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 registers8 / 2,210 ( < 1 % )
Total LABs4 / 221 ( 2 % )
Logic elements in carry chains0

この表は、 "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" でタイミング情報を調べてみました。

TypeSlackRequired TimeActual TimeFromToFrom ClockTo ClockFailed Paths
Worst-case tsuN/ANone0.390 nsgoq[0]~reg0--clk_b0
Worst-case tcoN/ANone22.055 nsq[7]~reg0q[7]clk_b--0
Worst-case thN/ANone-0.044 nsgoq[0]~reg0--clk_b0
Clock Setup: 'clk_b'N/ANoneRestricted to 304.04 MHz ( period = 3.289 ns )q[2]~reg0q[2]~reg0clk_bclk_b0

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 registers8 / 2,210 ( < 1 % )
Total LABs1 / 221 ( < 1 % )
Logic elements in carry chains7

同期式カウンタも8個の LE を使用しており、しかも一つの LAB で収まっています。 これは、 LE がカウンタや加算器を作りやすい構成になっているのためです。

タイミングは、こうなっています。

TypeSlackRequired TimeActual TimeFromToFrom ClockTo ClockFailed Paths
Worst-case tcoN/ANone7.142 nsq[5]~reg0q[5]clk_b--0
Clock Setup: 'clk_b'N/ANoneRestricted to 304.04 MHz ( period = 3.289 ns )q[0]~reg0q[7]~reg0clk_bclk_b0

この場合も300MHzのクロック入力に耐えることが出来ますが、クロックが変化してから q[5] が変化するまで 7.1ナノ秒の時間が必要です。 このことから、カウンタの値に意味がある場合の最大周波数は140MHzということになります。

考察

回路規模が小さくなることを期待して、リップル・キャリー・カウンタを合成しましたが、積極的に使いたくなる性能は出ませんでした。 CPLD を使っている限りは、同期式カウンタを使ったほうが良いようです。

参考文献

トランジスタ技術 (Transistor Gijutsu) 2008年 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で遊ぼう (9) [プログラム三昧]このエントリーを含むはてなブックマーク#

WS000219-320.png

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 が使用されていました。

WS000193.png

そこで、「テクノロジ・マップ」を表示させた結果がこれです。 これは、下位 4bit から上位 4bit にハーフキャリーを渡している箇所なのですが、なぜか、二つの LE がバッファとして使用されています。 これと同じバッファが最下位キャリーと最上位キャリーにも追加されているので、バッファに使用されている LE は合計4個もあります。 これに全加算器の分、8個を加えて12個という内訳です。

なぜ、バッファが挿入されたのかをオンライン・マニュアルで探しました。 まだ、はっきりとはしていませんが、キャリーが直接外部端子に出てくるときにバッファが挿入されているようです。 また、確認してみなきゃ。

参考文献

トランジスタ技術 (Transistor Gijutsu) 2008年 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) [プログラム三昧]このエントリーを含むはてなブックマーク#

WS000219-320.png

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 に入るというシフト・レジスタ状の動作をします。

WS000191.png

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;
    :
WS000192.png

RTL レベルでシミュレーションを行うと、こうなりました。 "push" がアサートされた時の動作を見ると、 datain の値が stack1 と stack2 に突き抜けているのがわかります。 実際の回路では、レーシングと呼ばれる現象です。

論理合成ではエラーが出ない

設計者にとっては、レーシングを起こす回路は誤りと認識されるのですが、機械にはわかりません。 そのため、論理合成時にはエラーが出ず、 stack1 と stack2 に datain の値が代入される HDL 記述どおりの回路ができてしまいます。

こればかりは、しっかりとテスト・パターンを書いて検証して気をつけるしかなさそうです。

参考文献

トランジスタ技術 (Transistor Gijutsu) 2008年 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で遊ぼう (7) [プログラム三昧]このエントリーを含むはてなブックマーク#

WS000219-320.png

トランジスタ技術誌に書かれていたインストラクション・デコーダは、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月号 [雑誌]

トランジスタ技術 (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) [プログラム三昧]このエントリーを含むはてなブックマーク#

WS000219-320.png

トランジスタ技術誌に「リセット信号生成回路」の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 が使用されています。 その使い道をテクノロジ・マップで見てみました。

WS000188.png

すると、6個の LE がカウンタ値の一致検出回路に消費されていることがわかりました。 16ビットのカウンタの値を比較しているので、これだけ大量の LE が必要なのです。

一致比較を大小比較に変更してみる

HDL 記述では、16ビットの値の一致検出を行っていました。 このリセット信号生成モジュールでは、リセット出力がネゲートされたらカウンタを停止させています。 そのため、無理に「一致検出」をする必要は無く、「大小比較」でも十分に同じ機能を得られるはずです。 そこで、一致検出部分を大小比較に変更してみました。

  :
  assign    count_en = (reset_count>=16'h8000)?(1'b1):(1'b0);
  :
WS000190.png

その結果、使用した 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月号 [雑誌]

トランジスタ技術 (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) [プログラム三昧]このエントリーを含むはてなブックマーク#

WS000219-320.png

前回の「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
WS000181.png

RTLによるシミュレーションを行うと、一行目のクロックに同期して2から5行目のフリップ・フロップの出力が変化します。 そのため、6行目の q 出力にグリッチが発生するはずがありません。

ゲート・レベル・シミュレーションの実行手順

トランジスタ技術誌でも、シミュレーションの方法が紹介されていましたが、RTLによる理想的な状態でのシミュレーションでした。 そのため、50GHzというむちゃくちゃなクロックを与えても回路が動作しているように見えました。 ここでは、合成後の状態でのシミュレーションの方法を説明します。

ゲート・レベル・ネットリストを作成する

WS000178.png

まず、 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" という遅延情報ファイルが作成されています。

シミュレーションを実行する

WS000185.png

ネットリストが揃ったので、シミュレーションを行います。 "ModelSim - Altera" を起動したら、メニューから "File → New → Project..." を選び "Create Project" ダイアログを開きます。 "Project Name" に適当なプロジェクト名を指定し、 "Project Location" には、 Quartus II で作成したプロジェクト・ディレクトリの下にある "simulation/modelsim" を指定します。 このディレクトリは、 Quartus II が作成したゲート・レベル・ネットリストが入るデフォルトのディレクトリです。

WS000184.png

すると、 "Add items to the Project" というダイアログが開くので、 "Add Existing File" をクリックし、シミュレーションに使用するネットリストを選択します。 ここで指定するファイルは、 "onehot.vo" と "tb_onehot.v" の二つです。

メニューから "Compile → Compile All" を選んでこれら二つのファイルをコンパイルします。 コンパイルすると、シミュレーションに使用可能なファイルが生成されます。

WS000186.png

シミュレーションを実行するには、メニューから "Simulate → Start Simulation..." を選択します。 すると、 "Start Simulation" ダイアログが開きます。 このダイアログで設定すべき箇所は二つです。

一つ目は、テストベンチのトップレベルの指定です。 これは、 "Design" タブで "work → tb_onehot" を選びます。

WS000187.png

二つ目は、 CPLD の素子などが収められたライブラリの指定です。 これは、 "Libraries" タブで "Search Libraries" にある "Add..." ボタンをクリックして、 "maxii_ver" を選択します。 これで、 "MAX II" 用に "verilog" で記述されたコンパイル済みライブラリを参照することができます。

以上で設定準備は完了です。 ダイアログの "OK" ボタンをクリックすると、シミュレーションが実行されます。

ゲート・レベル・シミュレーションの場合

WS000182.png

カウンタをゲートレベルシミュレーションにかけてみました。 伝達遅延情報が入っているので、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月号 [雑誌]

トランジスタ技術 (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) [プログラム三昧]このエントリーを含むはてなブックマーク#

WS000219-320.png

前回の「Quartusで遊ぼう (3)」で4値のアップ・ダウンカウンタを作成しました。 さて、このカウンタで論理合成後に使われているフリップ・フロップは、何個(何ビット)でしょうか?

ワン・ホット・コード

WS000177.png

図のテクノロジ・マップを見てわかるように、正解は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
WS000176.png

設定選択肢には、 "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月号 [雑誌]

トランジスタ技術 (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) [プログラム三昧]このエントリーを含むはてなブックマーク#

WS000219-320.png

前回の記事「Quartusで遊ぼう (2)」では、 1bit × 12word 構成の ROM を合成させましたが、どんな風に合成されたのかわかりませんでした。 きっと、調べる方法があるに違いないと考えて、マニュアルを紐解きました

ネットリスト・ビューワ

マニュアルを探した結果、「ネットリスト・ビューワ (Netlist Viewer) 」というものが存在することがわかりました。 名前の通りですと、単にテキストのネットリストが表示されるだけのような印象がありましたが、実は、そうではなかったようです。 ネットリスト・ビューワには、三つの種類があり、いずれのビューワも Task ペインの "Compile Design → Analysis & Synthesis → Netlist Viewers" から開くことができます。

RTLビューワ

WS000172.png

ひとつ目は、「RTLビューワ (RTL Viewer) 」と呼ばれるもので、Verilogのソース・コードを解析した結果を表示してくれます。 「Quartus で遊ぼう (1)」で作成した pikapika.v を表示させると、このようにグラフィカルに表示されます。

ステート・マシン・ビューワ

WS000173.png

二つ目は、「ステート・マシン・ビューワ (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

テクノロジ・マップ・ビューワ

WS000171.png

最後の「テクノロジ・マップ・ビューワ (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月号 [雑誌]

トランジスタ技術 (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) [プログラム三昧]このエントリーを含むはてなブックマーク#

WS000219-320.png

「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月号 [雑誌]

トランジスタ技術 (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) [プログラム三昧]このエントリーを含むはてなブックマーク#

WS000219-320.png

Altera の EPM2210F324 が ここここで話題になっているので、私も使ってみました。 ただし、ハードウェアは購入していないので、ソフトウェアで遊んだだけです。

DVDが待ちきれなかった

ダウンロードすべきファイルが、1Gバイトを超えるあまりにも巨大なものだったので、DVDを請求しました。 ところが、ちょうどソフトウェアのバージョンアップと重なったために、DVDの発送は、11月17日以降になるらしいことが書いてありました。 そのため、苦労して、ここからソフトウェアをダウンロードしてきました。

ダウンロードの方法には、二つあります。 一つは、ブラウザからHTTPで取り寄せる方法。 もう一つは、"Download Manager"という魅惑のダウンロードソフトを使うことです。 できたら、未知のソフトは使いたくなかったので、最初はHTTPを使ってダウンロードを試みました。 ところが、プロバイダか何かが制限をかけているらしく、600Mバイトを超えたところから急にバンド幅を制限されてしまい、終了まで十何時間かかるという表示が出てきてしまいます。 しかたなく、"Download Manager"を使うことにしました。 このソフトウェア、サーバに対して複数のコネクションを張り、あるコネクションにバンド幅制限をかけられたら、別のコネクションを張りなおしてダウンロードを続けているように見えます。 これじゃあ、プロバイダのバンド幅制限も効かないわけです。

インストールは、長いだけ

WS000215.png

本日、インストールしたのは、"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"をダブル・クリックすると、一連のコンパイルが始まりました。 コンパイルの結果が、このような表示になって表れました。

WS000220.png

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"でも十分ですね。

WS000217.png

シミュレーションの結果がこれです。 LEDの点滅周期は、

20n × 9 × 213 = 1.47456m [秒]

と、計算どおりです。 ただ、これでは、人間の目には点滅が見えません。 かといって、カウンタの分周比を大きくするとシミュレーションが終わらなくなるので、このあたりで妥協しておきます。

次の課題

トランジスタ技術によると、「マスクROMを構成させると、1ビットあたり1個のLABが必要になる」と書いてありました。 本当にLAB一個で1ビットの出力しか出てこないのであれば、ちょっとしたデコーダを作るだけで大量のLABを消費します。 このへんの真偽を確かめてみようと思います。

参考文献

トランジスタ技術 (Transistor Gijutsu) 2008年 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"が届きました。

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;

/**
 * This TcpDiscardServer class is a so-called DISCARD
 * server.  All received data will be discarded.
 * 
 * @author noritan
 */
public class TcpDiscardServer implements Runnable {

  private int port;

  /**
   * Construct a TcpDiscardServer 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 the inputStream
   * 
   * @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 で算数の穴埋め問題を解く [プログラム三昧]このエントリーを含むはてなブックマーク#

今日は、プログラミング・ネタです。 数年前に子供が持って帰った算数の宿題にこんな問題がありました。

2,3,4,6,8の五つの数字を使って、□.□□ × □.□ という掛け算を行ったとき、 答えが最小になるものと最大になるものを求めなさい。

人間が解くときには、理詰めで枝を刈りながら解くのですが、 計算機にやらせるときには、全ての組み合わせについて力任せに解きます。 ちょうど、カブレていた 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で掛け算の結果を求めて、 最小のものと最大のものをminvaluemaxvalueに 格納していきます。 実行結果は、あっというまに出てきます。

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 と なりました。

参考文献

図書館で借りて読みました。

初めてのPython 第2版

初めてのPython 第2版

  • 作者: マーク ルッツ
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2004/11
  • メディア: 単行本

こっちは、買ってしまいました。

Python Cookbook (Cookbook)

Python Cookbook (Cookbook)

  • 作者:
  • 出版社/メーカー: Oreilly & Associates Inc
  • 発売日: 2005/05/05
  • メディア: ペーパーバック

フラグをクリアするタイミングについて考える [プログラム三昧]このエントリーを含むはてなブックマーク#

よいテーマを与えていただいたので、少しばかり深く考えてみます。

発端は、某所のサンプルプログラムでした。 ソフトウェアで実現したフラグを割り込みサービスルーチンでセットし、通常のメインタスクでクリアするモデルです。

ソフトウェア・フラグの危険性

RxRingBufferSequence1.png

このモデルでは、2ステップでフラグをクリアします。

  1. メインタスクでは、フラグをクリアすべき条件を検出します。
  2. そして、フラグを実際にクリアする操作をします。

このため、両者のタイミングには時差が生じます。 その僅かな隙を割り込みに付け入られてしまうと、フラグをクリアすべき条件が崩れてしまいます。 つまり、クリアすべきでないフラグをクリアしてしまい、結果として、データを取りこぼしてしまうというわけです。

図の例では、青色のデータの処理中に緑色のデータが到着しています。 緑色のデータが到着したことを示すために、割り込み処理ルーチンでフラグがセットされます。 ところが、メイン・タスクでは、残された青色のデータの処理でフラグをクリアしてしまいます。 その結果、緑色のデータの到着が忘れ去られてしまい、データを取りこぼしてしまいます。

Javaにおける解決策

Javaでマルチスレッドなプログラムを書くと同じ問題が生じます。 Javaでは、セマフォが言語レベルで実装されているので、回避する事ができます。 ただし、デッドロックという別の問題があるので、よく分からずに使用するのは危険です。 参考文献には、マルチスレッドに関わる色々な解決策が記載されています。

HCS08における解決策

HCS08のような小規模なマイコンの場合、割り込みに付け入る隙を与えないようにするため、 メインタスクで割り込みを禁止するのは、簡単です。 マイコンに備わっている「割り込み禁止フラグ」をセットするだけです。 Cで記述する場合には、「DisableInterrupts」というマクロを使用する事ができます。

DisableInterrupts;
if (rx_buf_read_pointer==rx_buf_write_pointer) flags.rx_flag = 0;
EnableInterrupts;

禁止したままだと、永久に割り込みが発生しなくなりますので、用が済んだら割り込み許可「EnableInterrupts」をお忘れなく。

ハードウェア・フラグだって危険

フラグといえば、SCIのフラグも危険です。 例えば、データあり(RDRF)フラグは、データを準備するハードウェアがセットし、データを取り出すソフトウェアがクリアします。 しかも、ハードウェアがセットする行為は割り込みではありません。 これを禁止するには、SCIの機能を停止する以外に方法が無いので、ソフトウェアから禁止することは、事実上不可能です。

こんな危険性を考慮して、RDRFフラグをクリアするためには、ある面倒なシーケンスを踏む必要があります。

  1. フラグがセットされている時にSCIS1レジスタのRDRFフラグを読み出す。
  2. SCIDレジスタから到着したデータを取り出す。

このシーケンスの途中でフラグが再度セットされるような条件が発生したら、フラグをクリアするシーケンスは、キャンセルされ、フラグはセットされたままになります。 ちゃんとした、理由が有るのだから、このシーケンスをケチったりしては、いけません。

ColdFireの場合は、どうだろう

ここからは、私には未知の領域です。 ColdFireのような高級なマイコンには、バスエラーなどという仕組みが有って、命令の実行途中で例外(割り込み)が発生します。 そのため、割り込みを禁止したはずのソフトウェアフラグの処理中でも他のルーチンが動きだします。

これを防ぐためには、TASなどの本格的なセマフォを使用して、クリティカルエリアを指定した方がよいのでしょう。 こういう面倒な処理は、オペレーティングシステムに任せたいな。

また、ColdFireには、ユーザモードという概念があり、このモードでは、そもそも割り込みを禁止する領域を設定する事ができません。 リングバッファを使う場合には、やはり、オペレーティングシステムレベルで提供する必要があるという事ですね。

昔のプログラムの検証

今回の件で、その昔、HC11のために作ったリングバッファのプログラムを思い出しました。 調べたところ、しっかりと割り込みを禁止する領域を設定してありました。

                        *---------------------------------------------------------------*
                        *	Get a character through queue buffer.			*
                        *---------------------------------------------------------------*
fde4                    rxgetq	equ	*
fde4 3c                 	pshx
fde5 36                 	psha
fde6                    rxget1	equ	*
fde6 96 76              	ldaa	rxcnt
fde8 27 fc              	beq	rxget1			Wait for data.
fdea 0f                 	sei	------------------------No INT in this area.-----
fdeb 7a 00 76           	dec	rxcnt			Update counter.
fdee dc 6e              	ldd	rxgetp			Update get pointer.
fdf0 5c                 	incb
fdf1 c4 7f              	andb	#$7f			Clear Bit-7.
fdf3 d7 6f              	stab	rxgetp+1		Update LSB only.
fdf5 8f                 	xgdx				Move D to X.
fdf6 e6 00              	ldab	0,x			Get a character from queue.
fdf8 0e                 	cli	-------------------------------------------------
fdf9 32                 	pula
fdfa 38                 	pulx
fdfb 39                 	rts
                        *+++++ Check RDRF flag for RX +++++*
fe43                    scipro2	equ	*
fe43 1f 74 20 17        	brclr	scsr1,x $20 scipro3	Check RDRF.
fe47 e6 77              	ldab	scdr,x			Receive a character.
fe49 96 76              	ldaa	rxcnt			Update counter.
fe4b 2b 11              	bmi	scipro3			Ring buffer full.
fe4d 4c                 	inca
fe4e 97 76              	staa	rxcnt
fe50 18 8f              	xgdy				Save B register.
fe52 dc 70              	ldd	rxputp
fe54 5c                 	incb
fe55 c4 7f              	andb	#$7f			Clear Bit-7.
fe57 d7 71              	stab	rxputp+1		Update only LSB.
fe59 18 8f              	xgdy
fe5b 18 e7 00           	stab	0,y			Put a character.

まずは、ひと安心。

そういえば、これ以外に危ないプログラムを書いた記憶がないな。

参考文献

HCS08 Unleashed: Designer's Guide to the Hcs08 Microcontrollers

HCS08 Unleashed: Designer's Guide to the Hcs08 Microcontrollers

  • 作者: Fabio Pereira
  • 出版社/メーカー: Booksurge Llc
  • 発売日: 2007/11/13
  • メディア: ペーパーバック
増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編

増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編

  • 作者: 結城 浩
  • 出版社/メーカー: ソフトバンククリエイティブ
  • 発売日: 2006/03/21
  • メディア: 大型本

- | 次の20件 プログラム三昧 ブログトップ

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。