ログイン
ユーザ名:

パスワード:


パスワード紛失

新規登録
Main Menu
Tweet
Facebook
Line
:-?
« 1 (2) 3 4 »
スレッド表示 | 新しいものから 前のトピック | 次のトピック | 下へ
投稿者 スレッド
webadm
投稿日時: 2008-9-19 8:56
Webmaster
登録日: 2004-11-7
居住地:
投稿: 3092
RTL: CONTROL
次ぎはちょっと込み入ったCONTROLブロック。最初にスケルトンを入力した際にいくつかデバウンスする必要の無い信号等があったので見直しをかけた。



押しボタン入力のA,Bのみデバウンスを行うことにした。それ以外はチャッタリングが起こっても表示が一瞬乱れる以外には悪影響無し。

FREQUENCY COUNTERとCLOCK TIMER及びDISPLAYで必要な制御信号のみを生成するステートマシンから成る。



FREQUENCY COUNTER制御ステートマシンの状態遷移図。ACの立ち上がりで初期状態S=0に遷移し、RESET信号をアサートする。次ぎのC800HZクロックの立ち上がりでプリセット状態S=1に遷移しRESET信号をデアサートしPRESET信号をアサートすると同時に内部のカウンタに初期値をセットする。次ぎのC800HZクロックでカウンタイネーブル状態S=2に遷移しPRESETをデアサートしCEをアサートする。以降のクロックでカウンタ値が0でなければカウンタをデクリメントする。もしカウンタが0に達していたらロード状態S=3に遷移し、CEをデアサートしLOADをアサートする。次ぎのクロックで初期状態S=0に戻りLOADをデアサートする。



CLOCK TIMER制御ステートマシンの状態遷移図。ACの立ち上がりで初期状態IDLEへ遷移し、TIMMADJ,TIMHADJをデアサートする。以降のC10HZの立ち上がりでAもしくはBのどちらかがアサートされていればADJへ遷移しAとBの現在値をTIMMADJ,TIMHADJにそれぞれ保持する。次ぎのC10HZのクロックでAもしくはBが継続的にアサートされている場合には連続アサート期間を計測するためにカウンタ初期値をセットしてADJCへ遷移する。以降C10HZの立ち上がり毎に継続してAもしくはBがアサートされていてかつカウンタが0に達していない場合にはカウンタをデクリメントする。カウンタが0に達した場合にはADJRへ遷移し継続してアサートされているAもしくはBあるいはその両方に対応するTIMMADJ,TIMHADJの出力をキープする。以降のC10HZの立ち上がりでA及びB共にデアサートされた場合IDLEへ遷移しTIMMADJ,TIMHADJを0リセットする。

もう少し状態数の少ないステートマシンにしてAとBを独立して扱っても良い気がする。その場合リソースが余分にかかるかもしれない。



DISPLAY制御用ステートマシンの状態遷移図。基本的にACの立ち上がりでQ1状態へ遷移し、以降はC800HZの立ち上がり毎にQ1->Q2->Q3->Q4->Q1と循環する。DIMMER入力がアサートされていない場合には4サイクル間常にDUTY及びSTROBEはアサートされる。DIMMER入力がアサートされると1サイクル期間のみDUTY及びSTROBEがアサートされる。必要に応じてDUTYは蛍光表示管のプレート電圧制御に対応しSTROBEはグリッド電圧制御に対応する。従ってどちらもアサートしている間だけ表示が行われる。DUTYとSTROBEは同じ信号でもよさそうに思えるが、片方を少し早めにアサートし遅めにデアサートするような制御が実際には必要かもしれない。特にDUTYはセグメントに対応するプレート電圧を制御するのでグリッドがONになる前に予めプリチャージして、グリッドがOFFになってからプレート電圧をOFFするなど綺麗な点消灯のために微調整が出来るようにしてある。DISPLAYブロック側もこれに同期して4サイクル毎に桁スキャンを行う。



最後はデバウンサーサブモジュールの状態遷移。ACの立ち上がりでIDLE状態にリセットされ、C800HZの立ち上がりで入力信号dをサンプルし、アサートされていれば検出サイクル閾値をカウンタにセットしてDET状態へ遷移。入力がアサートされている限りC800HZの立ち上がり毎にカウンタ残りをチェックし0でなければカウンタをデクリメントする。入力信号がデアサートされることなくカウンタが0に至った場合には安定したとみなしてIN状態へ遷移し出力qをアサートする。次ぎのクロックでまだ入力がアサートされていればOUT状態へ遷移し以降クロック毎に入力がデアサートされるまで出力qをアサートし続ける。入力がデアサートされるとIDLEへ遷移し出力qをデアサートする。実際にどれくらいのサイクル数で安定したとみなすかは要調整。

カウンタをシステムリセット直後不定にならないようにすべてのモジュールでリセットがかかるように見直し、ボタン入力関係は独立のステートマシンを設けるようにしてRTLを記述した結果が以下の通り。

dbsm.v: デバウンサステートマシン

// Quartus II Verilog Template
// 4-State Mealy state machine

// A Mealy machine has outputs that depend on both the state and
// the inputs. When the inputs change, the outputs are updated
// immediately, without waiting for a clock edge. The outputs
// can be written more than once per state or per clock cycle.

module dbsm
(
input clk, in, reset,
output reg out
);

// Declare state register
reg [1:0]state;
reg [1:0]count;

// Declare states
parameter IDLE = 0, DET = 1, IN = 2, OUT = 3;
parameter DETCYCLE = 2'b11;

// Determine the next state synchronously, based on the
// current state and the input
always @ (posedge clk or negedge reset) begin
if (!reset)
begin
state <= IDLE;
count <= 0;
end
else
case (state)
IDLE:
if (in)
begin
state <= DET;
count <= DETCYCLE;
end
DET:
if (in)
begin
if(count)
count <= count - 1'b1;
else
state <= IN;
end
else
begin
state <= IDLE;
end
IN:
if (in)
begin
state <= OUT;
end
else
begin
state <= IDLE;
end
OUT:
if (in)
begin
state <= OUT;
end
else
begin
state <= IDLE;
end
endcase
end

// Determine the output based only on the current state
// and the input (do not wait for a clock edge).
always @ (state)
begin
case (state)
IDLE:
begin
out = 0;
end
DET:
begin
out = 0;
end
IN:
begin
out = 1;
end
OUT:
begin
out = 1;
end
endcase
end

endmodule

fcsm.v: FREQUENCY COUNTERステートマシン

module fcsm(clk, ac, reset, ce, preset, load);

// Input Port(s)
input clk, ac;

// Output Port(s)
output reg reset, ce , preset, load;

// Parameter Declaration(s)
parameter GATETIME = 6'b1111;
parameter S0 = 2'b00, S1 = 2'b01, S2 = 2'b11 , S3 = 2'b10;

// Additional Module Item(s)
reg [1:0] state;
reg [5:0] count;

always@(posedge clk or negedge ac)
begin
if(!ac)
begin
state <= S0;
count <= 0;
end
else
case(state)
S0:
begin
state <= S1;
end
S1:
begin
state <= S2;
count <= GATETIME;
end
S2:
if(count)
begin
count <= count - 1'b1;
end
else
begin
state <= S3;
end
S3:
begin
state <= S0;
end
endcase
end

always@(state)
begin
case(state)
S0:
begin
reset = 1;
ce = 0;
preset = 0;
load = 0;
end
S1:
begin
reset = 0;
ce = 0;
preset = 1;
load = 0;
end
S2:
begin
reset = 0;
ce = 1;
preset = 0;
load = 0;
end
S3:
begin
reset = 0;
ce = 0;
preset = 0;
load = 1;
end
endcase
end
endmodule


ctsm.v: CLOCK TIMERステートマシン

// Quartus II Verilog Template
// 4-State Mealy state machine

// A Mealy machine has outputs that depend on both the state and
// the inputs. When the inputs change, the outputs are updated
// immediately, without waiting for a clock edge. The outputs
// can be written more than once per state or per clock cycle.

module ctsm
(
input clk, in, reset,
output reg out
);

// Declare state register
reg [1:0]state;
reg [3:0]count;

// Declare states
parameter IDLE = 0, ADJ = 1, ADJC = 2, ADJR = 3;

// Determine the next state synchronously, based on the
// current state and the input
always @ (negedge clk or negedge reset) begin
if (!reset)
begin
state <= IDLE;
count <= 0;
end
else
case (state)
IDLE:
if (in)
begin
state <= ADJ;
end
ADJ:
if (in)
begin
state <= ADJC;
count <= 15;
end
else
begin
state <= IDLE;
end
ADJC:
if (in)
begin
if(count)
count <= count - 1'b1;
else
begin
state <= ADJR;
end
end
else
begin
state <= IDLE;
end
ADJR:
if (in)
begin
state <= ADJR;
end
else
begin
state <= IDLE;
end
endcase
end

// Determine the output based only on the current state
// and the input (do not wait for a clock edge).
always @ (state)
begin
case (state)
IDLE:
begin
out = 0;
end
ADJ:
begin
out = 1;
end
ADJC:
begin
out = 0;
end
ADJR:
begin
out = 1;
end
endcase
end

endmodule


control.v: CONTROLブロックRTLインプリメンテーション

module control(a, b, dimmer, ac, c800hz, c10hz,
ce, reset, preset, load, duty, strobe, timmadj, timhadj);

// Input Port(s)
input a, b, dimmer, ac, c800hz, c10hz;

// Output Port(s)
output ce, reset, preset, load, duty, strobe, timmadj, timhadj;

// Inout Port(s)

// Parameter Declaration(s)

// Additional Module Item(s)
wire db_a, db_b;

dbsm dmsm_a(c800hz, a, ac, db_a);
dbsm dmsm_b(c800hz, b, ac, db_b);
fcsm fcsm(c800hz, ac, reset, ce, preset, load);
ctsm ctsm_a(c10hz, db_a, ac, timmadj);
ctsm ctsm_b(c10hz, db_b, ac, timhadj);
dimsm dimsm(c800hz, dimmer, ac, duty, strobe);
endmodule

これらを合成した結果をRTL Viewerで見るとちょっと冗長のように見えるけど動けばよしとしよう。









webadm
投稿日時: 2008-9-22 10:27
Webmaster
登録日: 2004-11-7
居住地:
投稿: 3092
RTL: CLOCK TIMER
CLOCK TIMERブロックはおそらく一番ロジックの規模が多い部分。概略の検討は食事の合間に検討を続けメモ帳に書き留めて頭にものこっていたので気がついたらそのままQuartusを立ち上げてコードを入力していた。その時にいくつか細かな検討漏れが発覚し、やはりちゃんと内部ブロック図を描く必要があると反省。



概略を検討した時に漏れていたのは、FREQUENCY COUNTERの時は10進数でカウントすればよかったがCLOCK TIMERは60進数と12進数それに2進数を使い分けなければならないので10進数以外に2進数、6進数と12進数をサポートするように同期BCDカウンタを拡張する必要がある点と、どうしても分と時の調整のために1HZと10HZのクロックを切り替える必要からゲーテドクロック回路になってしまうがクロック選択切り替わりのタイミングをC10HZの立ち下がりに同期させないとヒゲが出て余分に更新されてしまう点。

しかし描いてみるとかなり規模が大きい。CPLDに入るのか心配になってきたが、最悪はCLOCK TIMERブロック無しで周波数表示のみでも構わないことにしよう。

bin2bcd.v: バイナリBCDデコーダー

module bin2bcd(bin, bcdout);

// Input Port(s)
input [3:0] bin;

// Output Port(s)
output reg [7:0] bcdout;

always@(*)
case(bin)
4'h0:
begin
bcdout[3:0] = bin;
bcdout[7:4] = 4'h0;
end
4'h1:
begin
bcdout[3:0] = bin;
bcdout[7:4] = 4'h0;
end
4'h2:
begin
bcdout[3:0] = bin;
bcdout[7:4] = 4'h0;
end
4'h3:
begin
bcdout[3:0] = bin;
bcdout[7:4] = 4'h0;
end
4'h4:
begin
bcdout[3:0] = bin;
bcdout[7:4] = 4'h0;
end
4'h5:
begin
bcdout[3:0] = bin;
bcdout[7:4] = 4'h0;
end
4'h6:
begin
bcdout[3:0] = bin;
bcdout[7:4] = 4'h0;
end
4'h7:
begin
bcdout[3:0] = bin;
bcdout[7:4] = 4'h0;
end
4'h8:
begin
bcdout[3:0] = bin;
bcdout[7:4] = 4'h0;
end
4'h9:
begin
bcdout[3:0] = bin;
bcdout[7:4] = 4'h0;
end
4'ha:
begin
bcdout[3:0] = 4'h0;
bcdout[7:4] = 4'h1;
end
4'hb:
begin
bcdout[3:0] = 4'h1;
bcdout[7:4] = 4'h1;
end
default:
begin
bcdout[3:0] = 4'hf;
bcdout[7:4] = 4'hf;
end
endcase

endmodule

ct.v: CLOCK TIMERモジュールRTLインプリメンテーション

module ct(ac, cs, timmadj, timhadj, c10hz, c1hz,
clcounter, ontimer, offtimer, sleeptimer, oncmpout, offcmpout, sleepout);

// Input Port(s)
input ac, timmadj, timhadj, c10hz, c1hz;
input [2:0] cs;

// Output Port(s)
output oncmpout, offcmpout, sleepout;
output [16:0] clcounter, ontimer, offtimer, sleeptimer;

// Inout Port(s)

// Parameter Declaration(s)

// Additional Module Item(s)
wire [20:0] clout;
wire [12:0] onout, offout;
wire clrst, onrst, offrst, slprst;
wire clsel, onsel, offsel, slpsel;
wire rst, madj, hadj;
wire clmclk, clhclk, onmclk, onhclk, offmclk, offhclk, slpmclk;
wire clmci, clhci, onmci, onhci, offmci, offhci, slpmci;
wire cls1co, cls10co, clm1co, clm10co, clhco, clpmco;
wire onm1co, onm10co, onhco, onpmco;
wire offm1co, offm10co, offhco, offpmco;

assign rst = (timmadj && timhadj);
assign madj = (timmadj && !timhadj);
assign hadj = (!timmadj && timhadj);

assign clsel = (cs == 3'b111)?1'b1:1'b0;
assign onsel = (cs == 3'b101)?1'b1:1'b0;
assign offsel = (cs == 3'b100)?1'b1:1'b0;

assign clrst = !ac || (clsel && rst);
assign onrst = !ac || (onsel && rst);
assign offrst = !ac || (offsel && rst);

assign clmclk = (clsel && madj)?c10hz:c1hz;
assign clhclk = (clsel && hadj)?c10hz:c1hz;
assign onmclk = (onsel && madj)?c10hz:c1hz;
assign onhclk = (onsel && hadj)?c10hz:c1hz;
assign offmclk = (offsel && madj)?c10hz:c1hz;
assign offhclk = (offsel && hadj)?c10hz:c1hz;

assign clmci = (clsel && madj)?1'b1:cls10co;
assign clhci = (clsel && hadj)?1'b1:clm10co;
assign onmci = (onsel && madj)?1'b1:cls10co;
assign onhci = (onsel && hadj)?1'b1:onm10co;
assign offmci = (offsel && madj)?1'b1:cls10co;
assign offhci = (offsel && hadj)?1'b1:offm10co;

sbuc #(4, 9) cls1(c1hz, 1'b1, clrst, 4'h0, clout[3:0], cls1co);
sbuc #(4, 5) cls10(c1hz, cls1co, clrst, 4'h0, clout[7:4], cls10co);
sbuc #(4, 9) clm1(clmclk, clmci, clrst, 4'h0, clout[11:8], clm1co);
sbuc #(4, 5) clm10(clmclk, clm1co, clrst, 4'h0, clout[15:12], clm10co);
sbuc #(4, 11) clh(clhclk, clhci, clrst, 4'h1, clout[19:16], clhco);
sbuc #(1, 1) clpm(clhclk, clhco, clrst, 1'b0, clout[20:20], clpmco);

sbuc #(4, 9) onm1(onmclk, onmci, onrst, 4'h0, onout[3:0], onm1co);
sbuc #(4, 5) onm10(onmclk, onm1co, onrst, 4'h0, onout[7:4], onm10co);
sbuc #(4, 11) onlh(onhclk, onhci, onrst, 4'h0, onout[11:8], onhco);
sbuc #(1, 1) onpm(onhclk, onhco, onrst, 1'b0, onout[12:12], onpmco);

sbuc #(4, 9) offm1(offmclk, offmci, offrst, 4'h0, offout[3:0], offm1co);
sbuc #(4, 5) offm10(offmclk, offm1co, offrst, 4'h0, offout[7:4], offm10co);
sbuc #(4, 11) offlh(offhclk, offhci, offrst, 4'h0, offout[11:8], offhco);
sbuc #(1, 1) offpm(offhclk, offhco, offrst, 1'b0, offout[12:12], offpmco);

assign clcounter[7:0] = clout[15:8];
bin2bcd clbin2bcd(clout[19:16], clcounter[15:8]);
assign clcounter[16:16] = clout[20:20];

assign ontimer[7:0] = onout[7:0];
bin2bcd onbin2bcd(onout[11:8], ontimer[15:8]);
assign ontimer[16:16] = onout[12:12];

assign offtimer[7:0] = offout[7:0];
bin2bcd offbin2bcd(offout[11:8], offtimer[15:8]);
assign offtimer[16:16] = offout[12:12];

assign oncmpout = (clout[19:8] == onout[11:0])?1'b1:1'b0;
assign offcmpout = (clout[19:8] == offout[11:0])?1'b1:1'b0;

endmodule

よく考えたらスリープタイマーは減算カウンタでないといけないのでSLEEPタイマーは実装しなかった。



実際には組み合わせ論理の一部が2ページにまたがっているが、主要な1ページ目だけで十分大きな規模だということがわかる。

だんだんと無謀で無駄なプロジェクトの様相を帯びてきた。周波数カウンタだけだったらPICで組んだ方がまったくもって賢い選択である。しかしバカだけど最後までやり遂げよう。
webadm
投稿日時: 2008-9-23 8:56
Webmaster
登録日: 2004-11-7
居住地:
投稿: 3092
RTL: DISPLAY
遂に最後のDISPLAYブロック。



基本的には蛍光表示管のダイナミック点灯制御が主になるが、それ以外に機能によって異なる小数点表示などの小細工が必要。

これを記述すれば入力から出力までのパスが通るのでやっとPlace & Routeが行われることになる。

やっとここまできたよママン(ノ∀`)

bcddec.v: BCD to 7segデコーダー

module bcddec(bcd, seg);

// Input Port(s)
input [3:0] bcd;

// Output Port(s)
output reg [6:0] seg;

always@(bcd)
begin
case(bcd)
4'b0000:
seg <= 7'b1000000; // a,b,c,d,e,f
4'b0001:
seg <= 7'b1111001; // b,c
4'b0010:
seg <= 7'b0100100; // a, b, d, e, g
4'b0011:
seg <= 7'b0110000; // a, b, c, d, g
4'b0100:
seg <= 7'b0011001; // b, c, f, g
4'b0101:
seg <= 7'b0010010; // a, c, d, f, g
4'b0110:
seg <= 7'b0000010; // a, c, d, e, f, g
4'b0111:
seg <= 7'b1111000; // a, b, c
4'b1000:
seg <= 7'b0000000; // a, b, c, d, e, f, g
4'b1001:
seg <= 7'b0010000; // a, b, c, d, f, g
default:
seg <= 7'b1111111;
endcase
end
endmodule

disp.v: DISPLAYブロックRTLインプリメンテーション

module disp(c3200hz, s, fcounter, clcounter, ontimer, offtimer, sleeptimer,
oncmpout, offcmpout, sleep, duty, strobe, ac, blanking,
stopwatch, ambcout, pmfmout, sega, segb, segc, segd, sege, segf, segg, point,
io, signalout, timerout, sleepout);

// Input Port(s)
input c3200hz, oncmpout, offcmpout, sleep, duty, strobe, ac, blanking;
input [2:0] s;
input [19:0] fcounter;
input [16:0] clcounter, ontimer, offtimer, sleeptimer;

// Output Port(s)
output stopwatch, ambcout, pmfmout, sega, segb, segc, segd, sege, segf, segg, point, signalout, timerout, sleepout;
output [4:0] io;

// Inout Port(s)

// Parameter Declaration(s)

// Additional Module Item(s)
reg [4:0] div16;
wire c200hz;
reg [19:0] counter;
reg ampm;
reg [3:0] bcd;
reg dot;
wire [6:0] seg;
reg [2:0] scan;
reg timer;

assign c200hz = div16[4];

always@(posedge c3200hz or negedge ac)
begin
if(!ac)
div16 <= 0;
else if(c3200hz)
div16 <= div16 + 1'b1;
end

always@(posedge c200hz or negedge ac)
begin
if(!ac)
scan <= 0;
else if(c200hz)
begin
if(scan == 3'b100)
scan <= 0;
else
scan <= scan + 1'b1;
end
end

always@(fcounter or clcounter or ontimer or offtimer or sleeptimer or s)
begin
case(s)
3'b111:
begin
counter <= {4'b1111,clcounter[15:0]};
ampm <= clcounter[16:16];
end
3'b101:
begin
counter <= {4'b1111,ontimer[15:0]};
ampm <= ontimer[16:16];
end
3'b100:
begin
counter <= {4'b1111,offtimer[15:0]};
ampm <= offtimer[16:16];
end
3'b110:
begin
counter <= {4'b1111,sleeptimer[15:0]};
ampm <= 0;
end
default:
begin
counter <= fcounter;
ampm <= 0;
end
endcase
end

always@(scan or counter or s)
begin
case(scan)
3'b000:
begin
bcd <= counter[3:0];
dot <= 0;
end
3'b001:
begin
bcd <= counter[7:4];
dot <= 0;
end
3'b010:
begin
bcd <= counter[11:8];
if(s[2])
dot <= 1'b1;
else
dot <= 0;
end
3'b011:
begin
bcd <= counter[15:12];
dot <= 0;
end
3'b100:
begin
bcd <= counter[19:16];
if(s[2]==0)
dot <= 1'b1;
else
dot <= 0;
end
default:
begin
bcd <= 4'b1111;
dot <= 0;
end
endcase
end

always@(posedge oncmpout or posedge offcmpout or negedge ac)
begin
if(!ac)
timer <= 0;
else if(oncmpout)
timer <= ~timer;
else if(offcmpout)
timer <= ~timer;
end

bcddec bcddec(bcd, seg);
assign sega = (blanking)?1'bz:(~duty | seg[0]);
assign segb = (blanking)?1'bz:(~duty | seg[1]);
assign segc = (blanking)?1'bz:(~duty | seg[2]);
assign segd = (blanking)?1'bz:(~duty | seg[3]);
assign sege = (blanking)?1'bz:(~duty | seg[4]);
assign segf = (blanking)?1'bz:(~duty | seg[5]);
assign segg = (blanking)?1'bz:(~duty | seg[6]);
assign point = (blanking)?1'bz:~(duty & dot);
assign ambcout = (blanking)?1'b0:~ampm;
assign pmfmout = (blanking)?1'b0:ampm;
assign io = (blanking)?{5{1'bz}}:~({5{duty}} & 1<<scan);
assign timerout = ~timer;

endmodule

あまりじっくり考えずに書いたので汚いコードになってしまっている。一応コンパイルとPlace & Routeは出来たけど規模がトラ技付録のMAXII基板には入らなかった(;´Д`)

BCDデコーダー以外は全部ひとつのソースに入れてしまったのでRTL Viewerで見ると2ページにまたがってしまった。もっと細かくサブブロックにわけて全体が1ページに収まるようにすると見通しが良くなると思う。





さてあとは適当に入力信号を設定して3.2768MHzのクロックを与えてリセットしてあげれば何らかの動きをするはず。シミュレーションが楽しみ。

webadm
投稿日時: 2008-9-24 3:36
Webmaster
登録日: 2004-11-7
居住地:
投稿: 3092
動作シミュレーション
しかしまったりと設計してきたPoorman's MSM5524もオリジナルの主要機能をカバーするものを一ヶ月ほどで設計できてしまった。ハードウェア記述言語と論理合成及びPlace & Routeツールのおかげである。

30年前にオリジナルのMSM5524を設計した人達はコンピューターも無かったから紙の上でほとんどの設計検討を行うしかない。論理合成ツールなのないのでひたすらカルノーマップを描いて自分で組み合わせ論理を合成していったのだろう。検証段階ではシミュレーターなど無いので標準ロジックICやトランジスタ回路を使って等価なプロトタイプハードウェアを製作し、実際に動かして観測することで確認していたと思われる。プロトタイプハードウェアで動作確認きたらそのネットリストにに基づいてトランジスタ回路に置き換え配置配線しマスクを描くという具合。当然最初のマスクで一発完動というのは少ないだろうから修正を重ねてようやく量産マスクが出来ると。今のソフトウェア開発がやっている人海戦術を昔のロジックLSI設計ではやっていたのだ。今日でもうそんなことをやっていたら会社は存続しないだろうけど、ソフトウェア開発会社は未だにそれをやらないと存続できないという哀れ。

余談に走ってしまった。

とりあえず最初に周波数カウンタモードに入力を設定し、クロックを入力し初回に全体リセットを入れてフリーランしたら出力信号がどうなるかシミュレーションしてみた。

予想では周波数カウントのゲートタイムは100分の8秒なので80msecこれを2回分程度見るとしてシミュレーション時間は200msecに設定。クロックを3.2768MHzで与えてみたつもり。シミュレーションには30分近く要した。



見るとセグメント出力と桁イネーブル信号は変化している。しかしduty比がDIMMERをディスエーブルにしているのに1/4になってしまっている。それに桁イネーブル信号とSEGGにヒゲが出ている。それとSW周波数カウントの時にはAM/PM表示はどちらもOFFにしないといけない気がする。

それと内部のC1HZとC10HZの周期がおかしい。よく考えたら3.2768MHzのクロック入力を生成する際に間違って10倍早い周期を与えていた。C1HZとC10HZの周期関係は期待した通り1:10になっている。まあコンパイルレポートを見ると動作クロックは数十MHzまで大丈夫くさいので10倍早いクロックでも動くということがわかった。

それと周波数カウンタ動作はどうもうまくいっていないらしい。

これから原因を探ることになるが、目的別に小さいテストベンチをいろいろ組んだ方がよさそうだ。

P.S

よく見たら入力信号のSの値がall Hで時計モードになっていた..orz

確かに表示がリセット時の初期値 AM 01:00だ。ちゃんと動いているじゃないか(´∀` )

少なくても表示が変化するのを見るには1分間以上シミュレートしないといけない、200msecで30分かかるから1秒間シミュレートするのに150分、1分間だとその60倍の9000分。150時間かかるという計算に、無理すぎ(;´Д`)

分替わり直前の状態から分替わりの前後をシミュレーションしないとやってられない。

もう一度周波数カウンタモードでやり直してみるか。
webadm
投稿日時: 2008-9-24 5:08
Webmaster
登録日: 2004-11-7
居住地:
投稿: 3092
Re: 動作シミュレーション
今度は改めて周波数カウンターモードでやってみた。外部クロック周期はちゃんと正しく1/3276800=305.2nsを与えた。しかし周波数カウンタ入力が意図した4.454MHzじゃなくて10倍の45.54MHzのままだった...orz



カウンタは当然ながらオーバーフローしているけどそれなりにカウント動作はしているようで、表示出力も合っている。

ちゃんと動くじゃないか(´∀` )

一発でここまで動くとは思わなかった。

次ぎはちゃんとFINを29.999MHzに同調した時の30.454MHzを1/10プリスケールした3.0454MHzを与えてやってみよう。

P.S

しかし良くみるとゲートタイムが短い気がする。80msecじゃなかったか、どうみても20msecでカウンター出力が変化している。
webadm
投稿日時: 2008-9-24 5:41
Webmaster
登録日: 2004-11-7
居住地:
投稿: 3092
周波数カウンターモードのシミュレーション
今一度入力条件を正しく与えて、周波数カウンタモードで動作シミュレーションしてみた。



結果はFREQUENCY COUNTERブロックの最上位桁が繰り上がりの際にあり得ないFになってしまっている。本当は1でないといけないはず。

どうやらPRESETのタイミングでカウンタの初期値がロードされていないし、カウンタがアップカウンタでなくダウンカウンター動作しているし(;´Д`)



デザインを見直した方がよさそうだ。
webadm
投稿日時: 2008-9-24 6:18
Webmaster
登録日: 2004-11-7
居住地:
投稿: 3092
QuartusのState Machine Viewer
ゲートタイムが短いのはRTL記述でパラメータ定数が間違っていたのが原因だった。修正してシミュレーションをやり直すとちゃんと80msecになった。



シミュレーション結果を良くみるとFREQUENCY COUNTER制御信号のうちRESET信号が極性が逆になっていることが発覚。

RTL記述では他の制御信号と同様に正論理で書いているのだが何故そうなる?

と思って超わかりづらいQuartusのState Machine Viewerを眺めていて気づいた。



RTL Viewerで当該State Machineのネットリストを見ると。



すなわちQuartusではステートマシンの状態ビットS0,S1,S2,S3をそれぞれダイレクトにRESET,PRESET,CE,LOADの4つの制御信号として出しているということである。しかしState Machineのエンコーディング表を見るとS0は状態S0の時のみ0でそれ以外1なので負論理である。本当は符号反転しないといけないのではないだろうか?

論理合成のバグか? またいきなり当たったのか?

Quartusのバージョンは最新の8sp1なのだが。

webadm
投稿日時: 2008-9-24 7:17
Webmaster
登録日: 2004-11-7
居住地:
投稿: 3092
どうやらバグではないようだ
RESET出力が負論理なのを無理矢理正論理に反転する記述を追加してコンパイルすると、何故かTechnology Map Viewerで見るとほとんどのFREQUENCY COUNTER内部ロジックが最適化によって無くなってしまう事態が発生。

なんだこれは?

どうやら負論理になっているのはQuartus自体が大域的に最適化した結果なのではないだろうかと思えてきた。

なにも内部信号をデザイナの意図に従わなくても論理が圧縮できるなら変えてしまえという最適化動作が選択されているのかもしれない。

とは言えRTL Viewerだけ見ると明らかにおかしい。

しかしTechnology Map Viewerで見るとちゃんとreset相当信号は負論理であることが前提になっているように見える。RTL Viewerの問題か?



なんともお騒がせな話だ。昔はRTL Viewerは結構信用できたんだけど、8.0になったらどうにもこうにも。

P.S

いや良くみたらTechnology Map Viewerのロジックもおかしいと思ったら全部下位ブロックを展開してみたら、最後の最後で論理反転していて帳尻があっていた..orz



わかりづらいよ。

ということでQuartusへの有らぬ疑いは晴れた。さて原因は他にあると。
webadm
投稿日時: 2008-9-24 9:10
Webmaster
登録日: 2004-11-7
居住地:
投稿: 3092
周波数カウント動作は出来た
どうやらPRESET時の初期値が0以外だとおかしなカウンタ論理になるらしいことが判明。とりあえず原理動作確認のために初期値はall0として、プリスケーラーが間違って分周しすぎていたのを修正して動作シミュレーションをやり直してみた。結果、与えていた2.9999MHz+45.5kHz=3.0454MHzがちゃんとカウント出来たヽ(´ー`)ノ



あとは初期値が0以外にした場合をやってみよう。初期値も当初間違えていて、455を足したら0になるBCD値でないといけないのをバイナリで計算していた。本来は100000-455=99545を与えないといけなかった。



なんとちゃんと正しく中間周波数分を差し引いた値(30454-455=29999)がカウントされるじゃないか。

結局、

・プリスケーラー値を間違えていた
・カウンタ初期値をBCDでなくバイナリ値で与えていた

のが原因だった..orz

自爆とはこのこと。

しかし今度のQuartusはPlace & Routeでかなりすごい最適化をするようになってシミュレーターの値を見ると意図したのとは全然違うのでバグではないかと誤解してしまう。外部から見た挙動は同じだが内部論理と挙動がまるで違っている。

今回の場合は、初期値に99545を指定すると、最後に出力ラッチする際にカウンタ値と初期値に対応したBCD値で排他的論理和を取っている。

すなわち初期値が9の桁のBCDカウンタはデザイン上では以下の様に変化することを意図していた

9->0->1->2->3->4->5->6->7->8->9->0

Quartusの最適化によってカウンタは初期値によらず常に0から開始し、以降指定された初期値と設計上の期待値と排他的論理和をとった値を遷移する特殊なカウンタに変更された。

デザイン上意図していたカウンタ出力の遷移

1001(9)->0000(0)->0001(1)->0010(2)->0011(3)->...

これに初期値1001(9)を排他的論理和をとった値を最適化されたカウンタは遷移する

0000(0)->1001(9)->1000(8)->1011(B)->1010(A)->...

という具合に一見すると変な遷移をするようになる。最終的に初期値である1001(9)と排他的論理和をとれば設計者が期待していたとおりのカウンタの出力が得られることがわかる。

つまり最終値はB0CDCとなった場合初期値99545を排他的論理和を取ると

1011 0000 1100 1101 1100
B 0 C D C
xor
1001 1001 0101 0100 0101
9 9 5 4 5

0010 1001 1001 1001 1001
2 9 9 9 9

という仕掛けになっていた。

いやまったくすごい、こういう最適化論理は人間が最初から意図してRTL記述するのは大変なので感心した。

周波数カウンタ動作はこれでよしとしよう(´∀` )

もうほとんど終わったも同然。

P.S

ちなみにシミュレーション時間は入力する信号の周波数が10分の1になったので劇的に短くなり、4分で終わるようになった。これだと1秒間分をやっても20分、1分間分だと20時間で終わることになる。
webadm
投稿日時: 2008-9-25 20:06
Webmaster
登録日: 2004-11-7
居住地:
投稿: 3092
時計とタイマー関連
今度は時計とタイマー関連の動作もちょっと見てみようと簡単にaボタンを押しっぱなしにして時計を早送り動作させてみた。



結果はそれらしく10HZ毎に分の表示が増加しているけど、ちょっとリピートが始まるタイミングが早すぎないか?

本来は連続1.6秒押されたら10HZでリピート更新なはず。

それとC1HZとC10HZの分周がおかしいのを今更気づく。コーディングミスってた(;´Д`)

リピートうんぬんの動作はctsm.vあたりに原因がありそう。



押しボタンを断続的にトグルしてみると、一回につきC10HZ周期の単一パルスが出るところは良いけれど、それによって2回カウントが変化している。一回目は意図した通りC10HZの立ち上がりだけど、2回目はTIMMADJ信号の立ち下がり、すなわちカウンタのクロックソースをC10HZからC1HZに戻すところで一旦C10HZが立ち下がった後にC1HZに切り替わりHに戻るため再び立ち上がりエッジが発生するということによる。

これはだめだな。

昔あるところで新人の外注設計者がプロセッサの低消費電力動作のための可変周波数クロックジェネレータを設計した時の事。クロック周波数設定を変更すると出力クロック信号にヒゲが出るということがデザインレビューで発覚して「だめだろこれは。誰だこんなきたない設計したのは?」とけちょんけちょんにこき下ろされていたのを思い出した。その後その新人君はある日突然姿を消した。
« 1 (2) 3 4 »
スレッド表示 | 新しいものから 前のトピック | 次のトピック | トップ

投稿するにはまず登録を
 
ページ変換(Google Translation)
サイト内検索