RTLに落としてシミュレーションしてる間にふと気づいたのがリフレッシュシーケンサの起動条件にリフレッシュクロックのエッジ(具体的にはL→Hの遷移)検出処理が抜けていたのに今頃気付く。脳内記憶合成→実装過程での漏れで上位工程でのミスだからステートダイヤグラムを修正する。
これに伴いgraphvizのdotファイルも当然修正する。
// 2021/11/06 : 復元
// 2021/11/07 : nDSACK追加
// 2021/11/08 : S5 -> S0 条件追加(/AS Negate)
// 2021/11/14 : R0,R1誤記訂正( refclkのエッジ検出漏れ )
digraph {
//rankdir=LR;
graph [ layout = dot ];
node [shape = doublecircle]; S0 S1 S2 S3 S4 S5 R0 R1 R2 R3 R4 R5 R6 R7;
node [shape = parallelogram]; CPUシーケンス RFSHシーケンス;
node [shape = ellipse]; DRAMCYC RFSHCYC;
node [shape = rarrow]; nRAS nCAS DRAMPX nRAS_RFSH nCAS_RFSH nDSACK;
CPUシーケンス -> S0 [ label = "CPU to DRAM制御" ];
S0 -> S1 [ label = "/AS & DRAMAREA & CPUCLK & /RFSHCYC" ];
S1 -> S2 ;
S2 -> S3 [ label = "CPUCLK" ];
S3 -> S4 ;
S4 -> S5 ;
S5 -> S0 [ label = "AS(Negate /AS)" ];
S1 -> DRAMCYC [ label = "Start DRAM cyc" ];
S5 -> DRAMCYC [ label = "End DRAM cyc" ];
S1 -> nRAS [ label = "Assert nRAS" ];
S4 -> nRAS [ label = "Negate nRAS" ];
S3 -> nCAS [ label = "nCAS" ];
S5 -> nCAS [ label = "nCAS" ];
S2 -> DRAMPX [ label = "DRAMPX->COL"];
S5 -> DRAMPX [ label = "DRAMPX->ROW"];
S4 -> nDSACK [ label = "Assert DSACK"];
S5 -> nDSACK [ label = "Negate DSACK"];
RFSHシーケンス -> R0 [ label = "DRAM RFSH制御" ];
R0 -> R1 [ label = "REFCLK" ];
R0 -> R0 [ label = "wait until REFCLK==H" ];
R1 -> R2 [ label = "AS & REFCLK & /CPUCLK & /DRAMCYC" ];
R2 -> R3 ;
R3 -> R4 ;
R4 -> R5 ;
R5 -> R6 ;
R6 -> R7 ;
R7 -> R0 ;
R2 -> nRAS_RFSH [ label = "Assert CBR RFSH nRAS" ];
R6 -> nRAS_RFSH [ label = "Negate CBR RFSH nRAS" ];
R1 -> nCAS_RFSH [ label = "Assert CBR RFSH nCAS" ];
R5 -> nCAS_RFSH [ label = "Negate CBR RFSH nCAS" ];
R1 -> RFSHCYC [ label = "Start RFSH cyc" ];
R7 -> RFSHCYC [ label = "End RFSH cyc" ];
}
R0、R1が修正した状態遷移。
上流が修正されたのだから下流のverilog,テストベンチ、シミュレーションパタンも必然的に修正、更新となる(あたりまえ)。
・修正したverilogコード
// 2021/11/07 : 作業開始
// 2021/11/09 : ベースカウンタをジョンソンに戻す。
// 2021/11/13 : address area definitions :
// 32bit dram : 0x80000000 - 0x81FFFFFF (32MB)
// 8bit psd : 0x00x00000 - 0x00x7FFFF
// 8bit rtc : 0xF0xx0000 - 0xF0xxFFFF
// 8bit 16550 : 0xF1xx0000 - 0xF1xxFFFF
// 8bit ft245 : 0xF2xx0000 - 0xF2xxFFFF
// 16bit pc104 : 0xFFxx0000 - 0xFFxxFFFF
// 2021/11/14 : psd,io空間のデコード(取り敢えず0ウェイトで試す)
// 2021/11/14 : rfshシーケンサのバグ修正(refclkのエッジ検出漏れ)
module dramc(sysclk,cpuclk,reset_b,a,as_b,ds_b,cpurw,refclk,ras_n,cas_n,ras_rfsh_n,cas_rfsh_n,dsack_n,psd_n,rtc_n,cs16550_n,ft245rd_n,ft245wr,iord_n,iowr_n);
input sysclk ; //system clock
input reset_b ; //system reset
input [31:24] a; // cpu address a[31:24] 上位8ビットデコード
input as_b ; // cpu address strobe
input ds_b ; // cpu data strobe
input cpurw ; // cpu read/~write
input refclk ; //refresh clock
output cpuclk ; //sysclk/2
output ras_n ; //ras
output cas_n ; // cas
output ras_rfsh_n ; // ras rfsh
output cas_rfsh_n ; // cas rfsh
output dsack_n ; // dsack
output psd_n ; // psd chip select
output rtc_n ; // rtc chip select
output cs16550_n ; // 16550 chip select
output ft245rd_n ; // FT245 read strobe
output ft245wr ; // FT245 write strobe
output iord_n,iowr_n ; // io read/write strobe
reg [2:0] drsq,ndrsq ; // dram sequencer
reg [3:0] rfsq,nrfsq ; // dram refresh sequencer
reg cpuclk ; // sysclk/2
parameter S0=3'b000,S1=3'b001,S2=3'b011,S3=3'b111,S4=3'b110,S5=3'b100 ; // dram state 0-5
parameter R0=4'b0000,R1=4'b0001,R2=4'b0011,R3=4'b0111,R4=4'b1111,R5=4'b1110,R6=4'b1100,R7=4'b1000 ; // rfsh state 0-7
parameter dram=8'h80,dram80=8'h80,dram81=8'h81,psd=8'h00,rtc=8'hf0,cs16550=8'hf1,ft245=8'hf2,pc104=8'hff ;
// cpuclock
always @( posedge sysclk or negedge reset_b ) begin
if ( ~reset_b )
cpuclk <= 1'b0 ;
else
cpuclk <= ~cpuclk ;
end
// state register
always @( posedge sysclk or negedge reset_b ) begin
if ( ~reset_b )
begin
drsq <= S0 ;
rfsq <= R0 ;
end
else
begin
drsq <= ndrsq ;
rfsq <= nrfsq ;
end
end
// dram state sequencer
always @( drsq or as_b or a or cpuclk or rfshcyc ) begin
case ( drsq )
// S0 : if ( as_b == 0 && {a[31:25],1'b0} == dram && cpuclk && rfshcyc == 0 )
S0 : if ( as_b == 0 && *1 && cpuclk && rfshcyc == 0 )
ndrsq <= S1 ;
else
ndrsq <= S0 ;
S1 : ndrsq <= S2 ;
S2 : if ( cpuclk )
ndrsq <= S3 ;
else
ndrsq <= S2 ;
S3 : ndrsq <= S4 ;
S4 : ndrsq <= S5 ;
S5 : if ( as_b )
ndrsq <= S0 ;
else
ndrsq <= S5 ;
endcase
end
always @( rfsq or as_b or cpuclk or dramcyc or refclk ) begin
case ( rfsq )
R0 : if ( refclk )
nrfsq <= R1 ;
else
nrfsq <= R0 ;
R1 : if ( as_b && refclk && ~cpuclk && ~dramcyc )
nrfsq <= R2 ;
else
nrfsq <= R1 ;
R2 : nrfsq <= R3 ;
R3 : nrfsq <= R4 ;
R4 : nrfsq <= R5 ;
R5 : nrfsq <= R6 ;
R6 : nrfsq <= R7 ;
R7 : nrfsq <= R0 ;
endcase
end
assign psd_n = ~(a == psd & ~as_b) ;
assign rtc_n = ~(a == rtc & ~as_b) ;
assign cs16550_n = ~(a == cs16550 & ~as_b) ;
assign ft245rd_n = ~(a == ft245 & ~as_b & ~ds_b & cpurw) ;
assign ft245wr = (a == ft245 & ~as_b & ~ds_b & ~cpurw) ;
assign iord_n = ~(~ds_b & cpurw) ;
assign iowr_n = ~(~ds_b & ~cpurw) ;
assign ras_n = ~drsq[1] ;
assign drampx = drsq[2] ;
assign cas_n = ~(~drsq[0] & drsq[2]) ;
assign dsack_n = ~*2 && cpuclk && rfshcyc == 0 )
ndrsq <= S1 ;
else
ndrsq <= S0 ;
S1 : ndrsq <= S2 ;
S2 : if ( cpuclk )
ndrsq <= S3 ;
else
ndrsq <= S2 ;
S3 : ndrsq <= S4 ;
S4 : ndrsq <= S5 ;
S5 : if ( as_b )
ndrsq <= S0 ;
else
ndrsq <= S5 ;
endcase
end
always @( rfsq or as_b or cpuclk or dramcyc or refclk ) begin
case ( rfsq )
R0 : if ( refclk )
nrfsq <= R1 ;
else
nrfsq <= R0 ;
R1 : if ( as_b && refclk && ~cpuclk && ~dramcyc )
nrfsq <= R2 ;
else
nrfsq <= R1 ;
R2 : nrfsq <= R3 ;
R3 : nrfsq <= R4 ;
R4 : nrfsq <= R5 ;
R5 : nrfsq <= R6 ;
R6 : nrfsq <= R7 ;
R7 : nrfsq <= R0 ;
endcase
end
assign psd_n = ~(a == psd & ~as_b) ;
assign rtc_n = ~(a == rtc & ~as_b) ;
assign cs16550_n = ~(a == cs16550 & ~as_b) ;
assign ft245rd_n = ~(a == ft245 & ~as_b & ~ds_b & cpurw) ;
assign ft245wr = (a == ft245 & ~as_b & ~ds_b & ~cpurw) ;
assign iord_n = ~(~ds_b & cpurw) ;
assign iowr_n = ~(~ds_b & ~cpurw) ;
assign ras_n = ~drsq[1] ;
assign drampx = drsq[2] ;
assign cas_n = ~(~drsq[0] & drsq[2]) ;
assign dsack_n = ~((~drsq[1] & drsq[2]) | ~psd_n | ~rtc_n | ~cs16550_n | (ft245 & ~as_b) ) ;
assign dramcyc = drsq[1] | drsq[2] ;
assign cas_rfsh_n = ~rfsq[1] ;
assign ras_rfsh_n = ~rfsq[2] ;
assign rfshcyc = rfsq[1] | rfsq[3] ;
endmodule
他にもちょこちょこ修正・追加が入っているがそれは後ほど(作業途中故)。
・テストベンチ
// 2021/11/07 : 作業開始
// 2021/11/09 : テストベンチ
// 2021/11/13 : task文に書き換え
// 2021/11/14 : io関連のテストベンチ追加
module dramc_tp ;
reg sysclk ; //system clock
reg reset_b ; //system reset
reg [31:24] a ; // cpu address
reg as_b ; // cpu address strobe
reg ds_b ; // cpu data strobe
reg cpurw ; // cpu read/~write
reg refclk ; //refresh clock
wire cpuclk ; //sysclk/2
reg rfshcyc ; //dram refresh cycle
wire ras_n ; //ras
wire cas_n ; // cas
wire ras_rfsh_n ; // ras rfsh
wire cas_rfsh_n ; // cas rfsh
wire dsack_n ; // dsack
wire psd_n,rtc_n,cs16550_n ;
wire ft245rd_n,ft245wr ;
wire iord_n,iowr_n ;
parameter STEP = 25000 ; // 25ns(25 x 1000ps)
parameter td1 = 15000 ; // 15ns
dramc dramc(sysclk,cpuclk,reset_b,a,as_b,ds_b,cpurw,refclk,ras_n,cas_n,ras_rfsh_n,cas_rfsh_n,dsack_n,psd_n,rtc_n,cs16550_n,ft245rd_n,ft245wr,iord_n,iowr_n);
always begin
sysclk = 1 ; #(STEP/2) ;
sysclk = 0 ; #(STEP/2) ;
end
task sim_init;
begin
as_b = 1 ; ds_b = 1 ; cpurw = 1 ; reset_b = 0 ; refclk = 0 ; a = 8'h00 ;
#(STEP*2);
reset_b = 1 ;
end
endtask
task dram_cyc ;
input [7:0] adr ;
begin
#(STEP);
a = adr ;
#(STEP);
as_b = 0 ;
#(STEP*2*3)
//$finish;
// wait(~dsack_n);
//#(STEP*2);
as_b = 1 ;
#(STEP*2);
end
endtask
task io_cyc ;
input [7:0] adr ;
input rw ;
begin
#(STEP);
a = adr ;
cpurw = rw ;
#(STEP);
as_b = 0 ;
if(cpurw)
ds_b = 0 ;
#(STEP);
if(~cpurw)
ds_b = 0 ;
#(STEP*2*2);
as_b = 1 ; ds_b = 1 ;
#(STEP*2);
end
endtask
task rfsh_cyc ;
begin
#(STEP*2);
refclk = 1 ;
// wait(~ras_rfsh_n)
#(STEP*2*3)
refclk = 0 ;
#(STEP*2);
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);
rfsh_cyc ;
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
$display("シミュレーション終了");
$finish ;
end
endmodule
task文の使い方が少し解るようになったのでテストベンチをtask文で検証対象単位に書き換えた。task文は関数定義のようなものと理解しているので使い方が多少なりとも解ると機能単位で閉じたテストベンチが組めるので具合が良い。
・gtkwaveによるシミュレーション
よくよく見るとRFSHCLKのL→Hの遷移を捉えてからリフレッシュシーケンサの起動を行っているのが解る。これが無いと毎回リフレッシュシーケンスが起動されてしまう酷いバグであった。原因は私の脳内忘却と思い込みです(毎度の事とは言え。。。)。
おまけでio関連のデコードを追加したのでそれを含めたシミュレーションパタンを示す。これは組み合わせ回路だから淡々と記す。
最初は1wait必要かと思って2ビットジョンソンのステートマシンを考えた(というか過去の脳内記録を元に復元)がPSD954とRTC,16550,FT245のアクセスタイムとRead/writeパルス幅を鑑みると0waitでも行けそうなので取り敢えず0wait(つまりは組み合わせ回路のみ)で構成してみた。
修正内容で合成したら組み合わせ回路追加の為か半ば期待通りマクロセルの消費量はそれ程でもなく
cpldfit: version P.20131013 Xilinx Inc.
Fitter Report
Design Name: dramc Date: 11-14-2021, 1:12PM
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
38 /108 ( 35%) 73 /540 ( 14%) 72 /216 ( 33%) 15 /108 ( 14%) 27 /69 ( 39%)
マクロセル使用率35%(38/108)、Pin使用率39%(27/69)とそう悪くもない印象。やはりXC9500の39入力と内部フィードバックルートのおかげで助かってるのではと勝手に思っている。まだ半分ぐらいの回路ブロック故コツコツ進めて行きたい。チップの想定構成は以下なイメージ
図中Timer/CounterはリフレッシュカウンタとBus error監視タイマとOS用のタイマを想定しているがとても収まらないので先述の通り最低限の構成にしてクロック供給は外付けのPIC12F1501等のCLC・タイマ付きシングルチップマイコンに肩代わりしてもらう構想。まだまだ先は長いな。。。
*1:a == dram80)|(a == dram81
*2:~drsq[1] & drsq[2]) | ~psd_n | ~rtc_n | ~cs16550_n | (ft245 & ~as_b) ) ;
assign dramcyc = drsq[1] | drsq[2] ;
assign cas_rfsh_n = ~rfsq[1] ;
assign ras_rfsh_n = ~rfsq[2] ;
assign rfshcyc = rfsq[1] | rfsq[3] ;
endmodule
// 2021/11/07 : 作業開始
// 2021/11/09 : ベースカウンタをジョンソンに戻す。
// 2021/11/13 : address area definitions :
// 32bit dram : 0x80000000 - 0x81FFFFFF (32MB)
// 8bit psd : 0x00x00000 - 0x00x7FFFF
// 8bit rtc : 0xF0xx0000 - 0xF0xxFFFF
// 8bit 16550 : 0xF1xx0000 - 0xF1xxFFFF
// 8bit ft245 : 0xF2xx0000 - 0xF2xxFFFF
// 16bit pc104 : 0xFFxx0000 - 0xFFxxFFFF
// 2021/11/14 : psd,io空間のデコード(取り敢えず0ウェイトで試す)
// 2021/11/14 : rfshシーケンサのバグ修正(refclkのエッジ検出漏れ)
module dramc(sysclk,cpuclk,reset_b,a,as_b,ds_b,cpurw,refclk,ras_n,cas_n,ras_rfsh_n,cas_rfsh_n,dsack_n,psd_n,rtc_n,cs16550_n,ft245rd_n,ft245wr,iord_n,iowr_n);
input sysclk ; //system clock
input reset_b ; //system reset
input [31:24] a; // cpu address a[31:24] 上位8ビットデコード
input as_b ; // cpu address strobe
input ds_b ; // cpu data strobe
input cpurw ; // cpu read/~write
input refclk ; //refresh clock
output cpuclk ; //sysclk/2
output ras_n ; //ras
output cas_n ; // cas
output ras_rfsh_n ; // ras rfsh
output cas_rfsh_n ; // cas rfsh
output dsack_n ; // dsack
output psd_n ; // psd chip select
output rtc_n ; // rtc chip select
output cs16550_n ; // 16550 chip select
output ft245rd_n ; // FT245 read strobe
output ft245wr ; // FT245 write strobe
output iord_n,iowr_n ; // io read/write strobe
reg [2:0] drsq,ndrsq ; // dram sequencer
reg [3:0] rfsq,nrfsq ; // dram refresh sequencer
reg cpuclk ; // sysclk/2
parameter S0=3'b000,S1=3'b001,S2=3'b011,S3=3'b111,S4=3'b110,S5=3'b100 ; // dram state 0-5
parameter R0=4'b0000,R1=4'b0001,R2=4'b0011,R3=4'b0111,R4=4'b1111,R5=4'b1110,R6=4'b1100,R7=4'b1000 ; // rfsh state 0-7
parameter dram=8'h80,dram80=8'h80,dram81=8'h81,psd=8'h00,rtc=8'hf0,cs16550=8'hf1,ft245=8'hf2,pc104=8'hff ;
// cpuclock
always @( posedge sysclk or negedge reset_b ) begin
if ( ~reset_b )
cpuclk <= 1'b0 ;
else
cpuclk <= ~cpuclk ;
end
// state register
always @( posedge sysclk or negedge reset_b ) begin
if ( ~reset_b )
begin
drsq <= S0 ;
rfsq <= R0 ;
end
else
begin
drsq <= ndrsq ;
rfsq <= nrfsq ;
end
end
// dram state sequencer
always @( drsq or as_b or a or cpuclk or rfshcyc ) begin
case ( drsq )
// S0 : if ( as_b == 0 && {a[31:25],1'b0} == dram && cpuclk && rfshcyc == 0 )
S0 : if ( as_b == 0 && ((a == dram80)|(a == dram81