迂遠を重ねてbus error例外検出・生成用ステートマシン(3ビットジョンソンだけど)をこしらえたので記す。
1.基本的な考え方
バスエラーは/ASがnegateされず(=dsack_nが所定時間以内にアサートされず)busがlockされるのを防ぐCPU側の仕掛けで具体的には/ASがアサートされてから所定時間内にネゲートされなければ/BERRをアサートする時間監視回路を構成すれば良い。この所定時間は適時適当で良く9〜12μS程度にする場合が多い(と思われる)。根拠はと言えばDRAMのリフレッシュサイクル(2ms/128cyc=15.6us)より短めの値でそのくらいなのでは?と勝手に考えているが極端に長い(数百usとか)で無ければ良い。Normal not ready系のバスアーキテクチャではbus errorの概念は有る(PCIとか)ので一般的と思われる。x86系は伝統的にNormal ready系が多いと思っているのでこの回路は実装されない(のでは?)。
2.実装方針
バスロック防止の為の安全回路故上記所定時間含めて実装方法は色々あるが例によってマクロセル消費を少なく楽に実装するつもりが色々迂遠する羽目になってしもうた。これは構成を真面目に考えてなかった(所謂詰めが甘い)のが原因で結局の処
2.1 バスエラー監視時間はリフレッシュクロック(16usを想定)を非同期データとしてそれの0.5〜1.5cycleを検出する。平たく言えばリフレッシュクロックのL->H->Lをステートマシンで検出して各ステートにas_bがネゲートされたら最初のステートに戻る(タイムアウトしないで正常にバスサイクル終了)をネチネチ組み込む羽目になった。
3.verilogコード(抜粋)
200行近くになってポイントが分からなくなってきたので該当するコードだけ抜き出す↓
reg [2:0] besq,nbesq ; // bus error sequencer
// state register
always @( posedge sysclk or negedge reset_b ) begin
if ( ~reset_b )
begin
drsq <= S0 ;
rfsq <= R0 ;
besq <= S0 ;
end
else
begin
drsq <= ndrsq ;
rfsq <= nrfsq ;
besq <= nbesq ;
end
end
// bus error sequence
always @( besq or refclk or as_b ) begin
case ( besq )
S0 : if ( as_b && ~refclk )
nbesq <= S0 ;
else
nbesq <= S1 ;
S1 : if ( ~as_b && refclk )
nbesq <= S2 ;
else
if( as_b )
nbesq <= S0 ;
S2 : if ( ~as_b && ~refclk )
nbesq <= S3 ;
else
if ( as_b )
nbesq <= S0 ;
S3 : if ( ~as_b && refclk )
nbesq <= S4 ;
else
if ( as_b )
nbesq <= S0 ;
S4 : nbesq <= S5 ;
S5 : if ( ~as_b )
nbesq <= S5 ;
else
nbesq <= S0 ;
endcase
end
assign berr_n = ~( ~besq[1] & besq[2] ) ;
ジョンソンの一つ覚えで毎度同様のパタンだが私にとってジョンソンカウンタやグレイコードカウンタは楽して安定したステートマシンを構成できる「定跡」故この程度の回路規模ではこの手が私には最適解と考えている。今時このような低レベルなコーディングはしないのかもしれないが所詮道楽故。
4.テストベンチ(抜粋)
parameter REF = 16*1000*1000; // 16us
always begin
refclk = 1 ; #(REF/2) ;
refclk = 0 ; #(REF/2) ;
end
task berr_cyc ;
input [7:0] adr ;
begin
#(STEP);
a = adr ;
#(STEP);
as_b = 0 ;
#(STEP);
wait(~berr_n) ;
#(STEP*2*4);
as_b = 1 ;
#(STEP*2*10);
end
endtask
initial begin
$display("シミュレーション開始");
$dumpfile("dramc_tp.vcd");
$dumpvars(0,dramc);
// $monitor("cpuclk = %b reset_b = %b as_b = %b dsack_n = %b refclk %b a = 8'h%x",cpuclk,reset_b,as_b,dsack_n,refclk,a);
sim_init ;
dram_cyc(8'h80);
dram_cyc(8'h81);
// dram_cyc(8'h12);
// io_cyc(8'h00,1);io_cyc(8'h00,0); // psd area
// io_cyc(8'hf0,1);io_cyc(8'hf0,0); // rtc area
// io_cyc(8'hf1,1);io_cyc(8'hf1,0); // 16550 area
// io_cyc(8'hf2,1);io_cyc(8'hf2,0); // ft245 area
// irq_cyc(7'b1000000);irq_cyc(7'b0100000);irq_cyc(7'b0010000);irq_cyc(7'b0001000);
// irq_cyc(7'b0000100);irq_cyc(7'b0000010);irq_cyc(7'b0000001);
berr_cyc(8'h12);
io_cyc(8'hf0,1);io_cyc(8'h0,1);
$display("シミュレーション終了");
$finish ;
end
berr_cyc(a31:24の上位8bit)が今回追加したtaskでwait文でberr_nがassertされる迄待つのでバグってると無限待ちになります(マズイ)。
4.iverilog+GTKwaveでのシミュレーション波形
berr_nのアサートタイミングがrefclkのエッジ(非同期)に依存するがそれは別途カウンタを設けても同じなので気にしない。。。
(ジジイには)みづらいのでprintイメージの白黒画面も付けておく。
5.合成結果(ise14.7 20131013版)
cpldfit: version P.20131013 Xilinx Inc.
Fitter Report
Design Name: dramc Date: 11-20-2021, 2:05AM
Device Used: XC95108-20-PC84
Fitting Status: Successful
************************* Mapped Resource Summary **************************
Macrocells Product Terms Function Block Registers Pins
Used/Tot Used/Tot Inps Used/Tot Used/Tot Used/Tot
46 /108 ( 43%) 91 /540 ( 17%) 87 /216 ( 40%) 21 /108 ( 19%) 42 /69 ( 61%)
マクロセル使用率 46/108(43%),Pin使用率 42/69(61%)とマクロセル緊縮財政効果が現れてる?かもしれない。まだPC104系バスインタフェースが未実装故(他の忘れてる・抜けてる機能含めて)インプリ不可能状況に陥る可能性は回避出来そうな気がしてきた。。。
6.その他
タイトルはこの構成に辿り着く迄にrefclk/4をカウントしたり/asで非同期クリアのカウンタ構成にしてみたり(大昔作ったLSIではこの部分だけ非同期回路であった。)sysclk/cpuclkから愚直にカウントしてみようと考えたり(マクロセル節約方針に反するので脳内却下したが)期待通りにシミュレーション出来なくてその原因を思いつかない場合に実態を確認せず思い込みで行動すると無為に時間と労力と気力を浪費してしまう教訓で最近の座右の銘ランク上昇中の言葉でもある。誰しも下手に埋没するのを潔しとせぬ故それに纏わる行為を努力と呼ぶのかもしれない(しみじみ)。