ステートダイヤグラム誤記訂正

 RTLに落としてシミュレーションしてる間にふと気づいたのがリフレッシュシーケンサの起動条件にリフレッシュクロックのエッジ(具体的にはL→Hの遷移)検出処理が抜けていたのに今頃気付く。脳内記憶合成→実装過程での漏れで上位工程でのミスだからステートダイヤグラムを修正する。

f:id:aki_iic:20211114135018p:plain

20211114:リフレッシュステートS0,S1修正(REFCLKエッジ検出処理漏れ)

 これに伴い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によるシミュレーション

f:id:aki_iic:20211114140135p:plain

20211114:

 よくよく見るとRFSHCLKのL→Hの遷移を捉えてからリフレッシュシーケンサの起動を行っているのが解る。これが無いと毎回リフレッシュシーケンスが起動されてしまう酷いバグであった。原因は私の脳内忘却と思い込みです(毎度の事とは言え。。。)。

 おまけでio関連のデコードを追加したのでそれを含めたシミュレーションパタンを示す。これは組み合わせ回路だから淡々と記す。

f:id:aki_iic:20211114140704p:plain

20211114 : io area timing (complex)

 最初は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入力と内部フィードバックルートのおかげで助かってるのではと勝手に思っている。まだ半分ぐらいの回路ブロック故コツコツ進めて行きたい。チップの想定構成は以下なイメージ

f:id:aki_iic:20211114142312p:plain

構築したいチップの機能構成要素(案)

 図中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