aki_iic’s blog

己の欲せざる処人に施す事無かれ、狂人の真似するは即ち狂人なり

師走

 いつの間にやら令和三年も師走を迎え政権交代も有り経済学科出身の財務省次官でありながらマクロ経済が分かりませんとタブロイド誌に投稿するようなあフォを更迭もせぬ財務省ファミリ(縁戚的な意味で)の岸田政権が元派閥領袖落選者(伸晃君の事だ)を登用したり相変わらず面白い(=おバカな)身内固めに奔走しておられるようだ。まあそれだけ(実力はともかく)安倍晋三の影に怯えているのかもしれないが実力以上に恐れられるのは政治家として(社会人としても)勲章なのかもしれない。

1.武漢肺炎のn番目の変種

 オミクロンか何かしらぬが憶えられないのでオミ株(仮称)と呼ぶ。見掛け倒しで品格も無く無生物思考で利権を守る点ではウィルス並なのかもしれない(元厚労省なのに、故か?)。このオミ株で盛り上げ様と躍起になってるメディア様だが我が国の国民(市民ではないよ)は流石に三度は騙されないようで(騙されるのは何事にも騙される「善い人」)まづは実態確認と信頼出来る政府責任者(跋扈してる更新しない統計マニアや自称専門家や医療利権代弁者を除く)からの公式情報を待ってそれに伴う適切な対処(三回目のワクチン接種など)に進むのが現時点での適切な処方箋なのだろう。メディア様の反省事項(まあ文系活動家馬鹿が経営層におわしますから「間違いを認めると死ぬ病」に長年罹患してるので治癒不可能だろうけど)はもし生き残っているのであれば理系なり科学知識と素養と論理的思考が可能な(メディア様らしくない)記者(外注でも)に助力頂いて義務教育を受けた国民が簡単に見破れる嘘やデマを流す現状の低レベル報道から脱却されるか撤退されては如何だろうか。馬鹿と活動家は死ななきゃ治らないから無駄かもしれないが。

2.電池車馬鹿

 先日日経Xtech(駄目テックとも)にノーベル賞受賞者の方がLIB市場拡大を訴えておられるとの記事があった↓

xtech.nikkei.com 有料記事なのでプレビューだけでご勘弁だが本文には(ご本人の趣旨がそのままテキストになっていると仮定すると)日本のLIB市場はハイブリッド車の為に小さすぎるので国策でLIB(つまりはEV市場)を拡大しないとガラパゴス(懐かしい言葉だ。一時期台湾シャープさんがブランドに使っておったな)化するのだそうだ。。。如何にも朝日新聞辺りが引用しそうなネタだが仮に本人が部品メーカとしての市場拡大を切望してると捉えるにしてもLIBの為にEV市場を国策で拡大せよとは私の価値観では「本末転倒で技術競争の市場原理を逸脱しており良識を疑う」というのが率直な感想です。いかにも属議員や経産省の一部やNEDOの如き利権大好き(でもアウトプットは。。。)が涎垂らして付いてきそうなパタン。LIB市場が拡大しないのは携帯機器市場が飽和して次世代需要の保存エネルギーは失速(エネファームとか)で見掛け好調で意識高い系(と新たな利権を求める方々)に支持されるEV市場拡大を(部品メーカとして)願ったと理解したがトヨタの社長さんが常々指摘してるように「EVが日本(というか一部を除く世界中)で売れないのは競合(従来)技術を凌駕する利点を見いだせない為に尽きる。換言すれば市場競争力が無い為であると断言してしまおう。市場は素直でその製品に市場価値があり且つ補助金等でそれを後押しするなら(麻生政権でやってたエコカー・エコポイント等)自然に市場は拡大するが現時点の電池車(電気自動車ではなく電池車:でんちくるま:と敢えて呼ばせて頂く)では価格・燃費・インフラ・運用コスト・車種・性能とあらゆる点で従来技術(ハイブリッド・クリーン(ガソリン|ディーゼル))に対する優位性が見当たらないように見えてしまうのは私の偏見だろうか? もし意識高い系の方々や日経BPさんがおっしゃるような素晴らしい技術が電池車であるならばとっくの昔に(何度目かのアナリスト・シンクタンクのEVのバラ色の未来(今は雑草レベルだけど)に近づいている筈なのだが。。。

3.Pin lockしてみた

 例のCPLDのデザインがだいぶ固まってきたのでPin lockを掛けてみた。

#PINLOCK_BEGIN

#Sat Dec  4 12:56:37 2021

NET "a<24>"          LOC =  "S:PIN18";
NET "a<25>"          LOC =  "S:PIN40";
NET "a<26>"          LOC =  "S:PIN6";
NET "a<27>"          LOC =  "S:PIN41";
NET "a<28>"          LOC =  "S:PIN71";
NET "a<29>"          LOC =  "S:PIN56";
NET "a<30>"          LOC =  "S:PIN65";
NET "a<31>"          LOC =  "S:PIN81";
NET "as_b"           LOC =  "S:PIN69";
NET "ba<23>"         LOC =  "S:PIN54";
NET "ba<24>"         LOC =  "S:PIN43";
NET "cpurw"          LOC =  "S:PIN50";
NET "ds_b"           LOC =  "S:PIN3";
NET "fc<0>"          LOC =  "S:PIN46";
NET "fc<1>"          LOC =  "S:PIN20";
NET "fc<2>"          LOC =  "S:PIN35";
NET "irq_n<1>"       LOC =  "S:PIN70";
NET "irq_n<2>"       LOC =  "S:PIN33";
NET "irq_n<3>"       LOC =  "S:PIN26";
NET "irq_n<4>"       LOC =  "S:PIN83";
NET "irq_n<5>"       LOC =  "S:PIN44";
NET "irq_n<6>"       LOC =  "S:PIN37";
NET "irq_n<7>"       LOC =  "S:PIN48";
NET "refclk"         LOC =  "S:PIN36";
NET "reset_b"        LOC =  "S:PIN74";
NET "sa<0>"          LOC =  "S:PIN15";
NET "sa<1>"          LOC =  "S:PIN21";
NET "sa<2>"          LOC =  "S:PIN67";
NET "siz<0>"         LOC =  "S:PIN34";
NET "siz<1>"         LOC =  "S:PIN11";
NET "sysclk"         LOC =  "S:PIN9";
NET "avec_n"         LOC =  "S:PIN25";
NET "berr_n"         LOC =  "S:PIN31";
NET "cas0_n"         LOC =  "S:PIN72";
NET "cas1_n"         LOC =  "S:PIN75";
NET "cas2_n"         LOC =  "S:PIN80";
NET "cas3_n"         LOC =  "S:PIN82";
NET "cf_ce1_n"       LOC =  "S:PIN47";
NET "cf_ce2_n"       LOC =  "S:PIN51";
NET "cf_iocs16_n"    LOC =  "S:PIN53";
NET "cf_iord_n"      LOC =  "S:PIN14";
NET "cf_iowr_n"      LOC =  "S:PIN84";
NET "ciin_n"         LOC =  "S:PIN61";
NET "cpuclk"         LOC =  "S:PIN63";
NET "cs16550_n"      LOC =  "S:PIN1";
NET "ft245rd_n"      LOC =  "S:PIN4";
NET "ft245wr"        LOC =  "S:PIN5";
NET "iord_n"         LOC =  "S:PIN66";
NET "iowr_n"         LOC =  "S:PIN68";
NET "ipl_n<0>"       LOC =  "S:PIN32";
NET "ipl_n<1>"       LOC =  "S:PIN39";
NET "ipl_n<2>"       LOC =  "S:PIN57";
NET "lan_n"          LOC =  "S:PIN7";
NET "n_dsack<0>"     LOC =  "S:PIN45";
NET "n_dsack<1>"     LOC =  "S:PIN55";
NET "psd_n"          LOC =  "S:PIN13";
NET "ras0_n"         LOC =  "S:PIN17";
NET "ras1_n"         LOC =  "S:PIN19";
NET "ras2_n"         LOC =  "S:PIN23";
NET "ras3_n"         LOC =  "S:PIN24";
NET "rtc_n"          LOC =  "S:PIN2";
#PINLOCK_END

 ISE14.7は2013年で進化が止まっているのでそれより更に昔に進化が止まってしまっているCPLDのツールはPin lock指示すると .ucfファイルにPin lock情報が上述の通り作成される。当然ながら中身を決める前に適当に入力した回路図のピンとは全くことなるもので回路図入力をやりなおす必要があるがこれは当然で配線リソースの少ないCPLDでデザイン前のPinに合わせるのは時間の無駄故ここは素直にEagleのサブスクを久しぶりに消費する事にしよう。

 

Dynamic Bus Sizing

 Simulation上はDRAMのタイミングも良さそうなので実装対象の16/32MB 72Pin SIMM(懐かし)用にras,casをデコードする。DRAMは32ビットポートでバイト単位のアクセスを許容するのでMotorolaのマニュアル風に言えばUU/UM/LM/LLをBig EndianでデコードしてSIMMのCAS0〜3に割り振る。

2.1 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のエッジ検出漏れ) 
// 2021/11/15 : 割り込みはautovector,irq7-0
// 2021/11/17 : bus error回路を非同期回路で再設計
// 2021/11/19 : 上手く行かないので同期回路+外部クロックで再構築
// 2021/11/21 : pc104というかCompact Flash追加
//                16bit cf : 0xFExx0000 - 0xFExxFFFF
// 2021/11/23 : iosq修正、テストベンチ修正
//            : マクロセル節約の為nwcnt <= wcnt をダウンカウントから
//            : アップカウント(インクリメンタ)に変更(2MC節約)
// 2021/11/25 : 試行錯誤したがwcnt系のデザインがMC最小(それでも71MC)故戻す
// 2021/11/28 : sa[2:0],siz[1:0],[1:0]n_dsack ,ras0-3_n,cas0-3_n追加
//            : ciin_n追加
 
module dramc(sysclk,cpuclk,reset_b,a,ba,sa,siz,as_b,ds_b,cpurw,refclk,ras0_n,ras1_n,ras2_n,ras3_n,cas0_n,cas1_n,cas2_n,cas3_n,n_dsack,psd_n,rtc_n,cs16550_n,ft245rd_n,ft245wr,iord_n,iowr_n,irq_n,fc,avec_n,ipl_n,berr_n,cf_ce_n,cf_iocs16_n,cf_iord_n,cf_iowr_n,ciin_n);
    input sysclk ; //system clock
    input reset_b ; //system reset
    input [31:24] a; // cpu address a[31:24] 上位8ビットデコード
    input [24:23] ba; // dram bank address ( 4 x 8MB SIMM想定 )
    input [2:0] sa; // cpu address sa[2:0] 下位3ビット
    input [1:0] siz; // cpu bus size
    input as_b ; // cpu address strobe
    input ds_b ; // cpu data strobe
    input cpurw ; // cpu read/~write
    input refclk ; //refresh clock
    input [7:1] irq_n ; // irq7-1
    input [2:0] fc ; // cpu function fc2-0

    output cpuclk ; //sysclk/2
    output ras0_n,ras1_n,ras2_n,ras3_n ; //ras
    output cas0_n,cas1_n,cas2_n,cas3_n ; // cas
//    output ras_rfsh_n ; // ras rfsh
//    output cas_rfsh_n ; // cas rfsh
    output [1:0] n_dsack ; // cpu dsack0,1
    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
    output avec_n ; // to cpu autovector
    output [2:0] ipl_n ; // to cpu interrupt request
    output berr_n ; // to cpu bus error
    output    cf_ce_n ;    // CF chipe enable
    output    cf_iocs16_n ;    // CF iocs16
    output    cf_iord_n,cf_iowr_n ; // CF ioread,iowrite
    output ciin_n ; // cpu cash inhibit input

    reg [2:0] ipl_n ; 
    reg [2:0] drsq,ndrsq ; // dram sequencer
    reg [3:0] rfsq,nrfsq ; // dram refresh sequencer
    reg [2:0] besq,nbesq ; // bus error sequencer
    reg [2:0] iosq,niosq ; // pc104 bus cycle sequencer
    reg [2:0] wcnt,nwcnt ; // wait cycle counter
    reg cpuclk ; // sysclk/2

    wire ras,ras_rfsh,cas,cas_rfsh ; // 
    wire dramcyc,rfshcyc ; // internal node (wire)
//    wire dsack_n ; // dsack

    parameter C0=2'b00,C1=2'b01,C2=2'b11,C3=2'b10 ; // 2bit johnson state 0-3
    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,cf=8'hfe,pc104=8'hff ;
    parameter IRQ7=7'b0111111,IRQ6=7'b1011111,IRQ5=7'b1101111,IRQ4=7'b1110111,IRQ3=7'b1111011,IRQ2=7'b1111101,IRQ1=7'b1111110 ;
    parameter LVL7=3'b000,LVL6=3'b001,LVL5=3'b010,LVL4=3'b011,LVL3=3'b100,LVL2=3'b101,LVL1=3'b110 ;
    parameter w1=3,w2=3,w3=3 ;

// 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 ;
                besq <= S0 ;
                iosq <= S0 ;
                wcnt <= 0 ;
            end
        else
            begin
                drsq <= ndrsq ;
                rfsq <= nrfsq ;
                besq <= nbesq ;
                iosq <= niosq ;
                wcnt <= nwcnt ;
            end
    end

// CF bus cycle sequence
    always @( cpuclk or iosq or a or as_b ) begin
        case ( iosq )
            S0 : if ( as_b == 0 && a == cf ) begin
                    nwcnt <= 0 ; //4'd4 ;
                    niosq <= S1 ;
                end
                else
                    niosq <= S0 ;
            S1 : if ( wcnt != w1 ) begin
                     nwcnt <= wcnt + 1 ;
                     niosq <= S1 ;
                end
                else begin
                    nwcnt <= 0 ; 
                    niosq <= S2 ;
                end
            S2 : if ( wcnt != w2 ) begin
                    nwcnt <= wcnt + 1 ;
                    niosq <= S2 ;
                end
                else
                    niosq <= S3 ;
            S3 : if ( ~cpuclk )
                    niosq <= S3 ;
                else begin
                    niosq <= S4 ;
                    nwcnt <= 0 ;
                end
            S4 : if ( wcnt != w3 ) begin
                    nwcnt <= wcnt + 1 ;
                    niosq <= S4 ;
                end
                else
                    niosq <= S5 ;
            S5 : if ( as_b )
                    niosq <= S0 ;
                else
                    niosq <= S5 ;
        endcase
    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
            
// 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 :    if ( refclk )
                    nrfsq <= R7 ;
                else
                    nrfsq <= R0 ;
        endcase
    end

    always @(irq_n) begin
        if (irq_n[7] == 1'b0) ipl_n = LVL7 ;
        else if (irq_n[6] == 1'b0) ipl_n = LVL6 ;
        else if (irq_n[5] == 1'b0) ipl_n = LVL5 ;
        else if (irq_n[4] == 1'b0) ipl_n = LVL4 ;
        else if (irq_n[3] == 1'b0) ipl_n = LVL3 ;
        else if (irq_n[2] == 1'b0) ipl_n = LVL2 ;
        else if (irq_n[1] == 1'b0) ipl_n = LVL1 ;
    end

    assign cf_ce_n = ~( ( iosq[0] | iosq[2] ) & ~as_b ) ;
    assign cf_iocs16_n = ~( ( iosq[0] | iosq[2] ) & ~as_b ) ;
    assign cf_iord_n = ~( ( iosq[1] | iosq[2] ) & ~as_b & cpurw ) ;
    assign cf_iowr_n = ~( iosq[1] & ~as_b & ~cpurw) ;
    assign berr_n = ~( ~besq[1] & besq[2] ) ;            
    assign avec_n = ~*2 & cas) | cas_rfsh);
    assign cas1_n = ~((((sa[1:0]==2'b10)|(~sa[1]&~siz[0]&~siz[1])|(siz[1]&siz[0]&~sa[1])|(~siz[0]&~sa[1]&sa[0])) & cas) | cas_rfsh);
    assign cas2_n = ~((((sa[1:0]==2'b01)|(~siz[0]&~sa[1])|(siz[1]&~sa[1])) & cas) | cas_rfsh);
    assign cas3_n = ~(((sa[1:0]==2'b00) & cas) | cas_rfsh);
//    assign dsack_n = ~*3 ;
    assign dramcyc = drsq[1] | drsq[2] ;
//    assign cas_rfsh_n = ~rfsq[1] ;
//    assign ras_rfsh_n = ~rfsq[2] ;
    assign rfshcyc = rfsq[1] | rfsq[2] ;
    assign ciin_n = ~(a[31:28]==8'hf);

endmodule

 コードが散らばって編集めんどいので丸ごと貼ります。

2.2 テストベンチ

`timescale 1ns / 100ps
// 2021/11/07 : 作業開始
// 2021/11/09 : テストベンチ
// 2021/11/13 : task文に書き換え
// 2021/11/14 : io関連のテストベンチ追加
// 2021/11/15 : 割り込み関連のテストベンチ追加
// 2021/11/19 : 気を取り直してbus error
// 2021/11/23 : `timescale 1ns/100psに設定

module dramc_tp ;
    reg sysclk ; //system clock
    reg reset_b ; //system reset
    reg [31:24] a ; // cpu address
    reg [24:23] ba ; // dram bank address (4x8MB)
    reg [2:0] sa ; // cpu a2-a0
    reg [1:0] siz ; // cpu bus size
    reg as_b ; // cpu address strobe
    reg ds_b ; // cpu data strobe
    reg cpurw ; // cpu read/~write
    reg refclk ; //refresh clock
    reg [7:1] irq_n ; // interrupt request
    reg [2:0] fc ; // cpu function code

    wire cpuclk ; //sysclk/2
    reg rfshcyc ; //dram refresh cycle
    wire ras0_n,ras1_n,ras2_n,ras3_n ; //ras
    wire cas0_n,cas1_n,cas2_n,cas3_n ; // cas
//    wire ras_rfsh_n ; // ras rfsh
//    wire cas_rfsh_n ; // cas rfsh
    wire [1:0] n_dsack ; // dsack
    wire psd_n,rtc_n,cs16550_n ;
    wire ft245rd_n,ft245wr ;
    wire iord_n,iowr_n ;
    wire [2:0] ipl_n ; // cpu ipl2-0
    wire avec_n ; // cpu autovector
    wire berr_n ;
    wire cf_ce_n,cf_iocs16_n,cf_iord_n,cf_iowr_n ;
    wire ciin_n ;

parameter STEP = 25 ; // 25ns(25 x 1000ps)
parameter td1  = 15 ; // 15ns
parameter REF  = 16*1000; // 16us

dramc dramc(sysclk,cpuclk,reset_b,a,ba,sa,siz,as_b,ds_b,cpurw,refclk,ras0_n,ras1_n,ras2_n,ras3_n,cas0_n,cas1_n,cas2_n,cas3_n,n_dsack,psd_n,rtc_n,cs16550_n,ft245rd_n,ft245wr,iord_n,iowr_n,irq_n,fc,avec_n,ipl_n,berr_n,cf_ce_n,cf_iocs16_n,cf_iord_n,cf_iowr_n,ciin_n);

always begin
    sysclk = 1 ; #(STEP/2) ;
    sysclk = 0 ; #(STEP/2) ;
end
always begin
    refclk = 1 ; #(REF/2) ;
    refclk = 0 ; #(REF/2) ;
end

task sim_init;
begin
    as_b = 1 ; ds_b = 1 ; cpurw = 1 ; reset_b = 0 ; a = 8'h00 ; refclk = 0 ; ba = 0 ; sa = 0 ;
    irq_n = 7'b1111111 ; fc = 3'b000 ;
#(STEP*2);
    reset_b = 1 ;
end
endtask

task dly16n ;
    input [7:0] n ;
begin
    repeat(n)
        #(REF) ;
end
endtask

task dram_cyc ;
    input [7:0] adr ;
    input [24:23] badr ;
    input [2:0] sadr ;
    input [1:0] size ;
    input [7:0] n ;

repeat(n)
begin
//    wait(~rfshcyc);
#(STEP);
    a = adr ; ba = badr & 2'b11 ; sa = sadr & 2'b11 ; siz = size ;
#(STEP);
    as_b = 0 ; ds_b = 0 ;
///#(STEP*2*3)
//$finish;
    wait(n_dsack==2'b00) ;//    wait(~dsack_n) ;
#(STEP*2);
    as_b = 1 ; ds_b = 1 ;
#(STEP*2);
end
endtask

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


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 cf_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 ;
//    wait(~dsack_n);
#(STEP*2*7);
    as_b = 1 ; ds_b = 1 ;
#(STEP);
    cpurw = 1 ;
#(STEP*3);
end
endtask

task 
irq_cyc ;
    input [7:1] irq ;
begin
    irq_n = ~irq ;
#(STEP*2)
    fc = 3'b111 ;
    as_b = 0 ;
#(STEP*2*3)
    as_b = 1 ;
#(STEP*2*4)
    irq_n = 7'b1111111 ;
    fc = 3'b000 ;
#(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,0,0,100);
repeat(1)
begin
    dram_cyc(8'h80,0,0,0,1);dram_cyc(8'h80,1,1,1,1);dram_cyc(8'h80,2,2,2,1);dram_cyc(8'h80,3,3,1,1);
end
//    dram_cyc(8'h81,0,0,0,1);
//    dram_cyc(8'h12,0,0,0,1);
    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);
    cf_cyc(8'hfe,1);cf_cyc(8'hfe,0);
//    dly16n(2) ;
    $display("シミュレーション終了");
    $finish ;
end

endmodule

 task文に慣れてくると関数イメージでネストも可能で書式も簡潔故徐々にパラメタ増やしてtask文を書くようになった。テストベンチといってもAssertしてる訳では無いから「僕の考える(期待する)シミュレーション波形を見たい呪文」に過ぎないがiverilog速いしgtkwaveで .vcdファイルをreloadして期待値迄繰り返す。

3.iverilog + gtkwaveによるシミュレーション波形

f:id:aki_iic:20211128111356p:plain

dynamic bus sizing

 ポートサイズを32bit→8bit→16bit→8bitと変化させてDynamic bus sizing風の動作をシミュレーションしている(つもり)。M68020,68030はDynamic bus sizingをCPU側でサポートしてたので回路が簡単になって助かった記憶がある。今回はDRAMは32ビット、psd含むioポートは8ビット、CF等のPC104系は16ビットポートとして返す様に設計している。まあBus sizingはCPUが勝手にやってくれるのでデバイス側が自分のポートサイズを[1:0]dsack_n にエンコードして返せば良いだけなのだが。

4.合成結果

cpldfit:  version P.20131013                        Xilinx Inc.
                                  Fitter Report
Design Name: dramc                               Date: 11-28-2021, 10:54AM
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       
80 /108 ( 74%) 179 /540  ( 33%) 148/216 ( 69%)   33 /108 ( 31%) 58 /69  ( 84%)

 マクロセル使用率80/108(74%),Pin使用率58/69(84%)と面倒臭い組み合わせ回路の割にマクロセル消費が少ない印象でデバイスアーキテクチャ上他入力組み合わせ回路(デコード系など)に特化したCPLDの良さが出ているのではと我田引水(obsoluteだけど)。経験上XC9500系のマクロセル使用率はピンフリーであれば90%台でも何とかなった印象だが土壇場で万歳は出来ないから(それは基板・回路構成再設計を意味するので)慎重に進める所存。

 

*1:a == dram80)|(a == dram81

*2:fc == 3'b111) & ~as_b) ;
    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 = drsq[1];
    assign ras_rfsh = rfsq[2];
    assign ras0_n = ~(((ba[24:23]==2'b00) & ras) | ras_rfsh);
    assign ras1_n = ~(((ba[24:23]==2'b01) & ras) | ras_rfsh);
    assign ras2_n = ~(((ba[24:23]==2'b10) & ras) | ras_rfsh);
    assign ras3_n = ~(((ba[24:23]==2'b11) & ras) | ras_rfsh);
    assign drampx = drsq[2] ;
    assign cas = (~drsq[0] & drsq[2]);
    assign cas_rfsh = rfsq[1] ;
    assign cas0_n = ~((((sa[1:0]==2'b11)|(sa[0]&siz[0]&siz[1])|(~siz[0]&~siz[1])|(sa[1]&siz[1]

*3:~drsq[1] & drsq[2]) | (~iosq[0] & iosq[2]) | ~psd_n | ~rtc_n | ~cs16550_n | ( ft245 & ~as_b ) ) ;
    assign n_dsack[0] = ~((~drsq[1] & drsq[2]) | ~psd_n | ~rtc_n | ~cs16550_n | ( ft245 & ~as_b ) ) ;
    assign n_dsack[1] = ~((~drsq[1] & drsq[2]) | (~iosq[0] & iosq[2]

Compact Flash(というかPC104)風iocycleとマクロセルメタボリックシンドローム

 Compact Flash(以下CF)もobsolute化進行中のストレージだがSDcardコントローラを書く(ハード/ソフト含めて)能力も無く伝統のTrue IDE modeを想定する。

1.基本タイミング

 ネットで拾ったCFのデータシート(確か日立製作所)からTrue IDEのタイミングを以下に引用する。

・True IDE mode read timing

f:id:aki_iic:20211127161114p:plain

true ide mode read

・True IDE mode write timing

f:id:aki_iic:20211127161208p:plain

true ide mode write

 あまりの遅さに若い方は失神されるかもしれないが20世紀のデバイスはこんなもんだったんです。なのでそれに合わせる為に(主にtsuA(IORD/IOWR),tw(IORD/IOWR)を満たす為のタイミング生成(とdsack生成)がステートマシンのお仕事になる。

2.ステートダイヤグラム

 今回はiverilog-gtkwaveで試行錯誤した構成から逆生成したので正確さはあるが緊張感に欠けるかもしれない。

f:id:aki_iic:20211127161911p:plain

cf iosq (graphviz)

graphvizのコード( .dot )

// 2021/11/27 : verilogコードとgtkwaveから逆生成
// 2021/11/23 : CF用iosqを再整理、特にdsack
// ステートの割当て
//S0(000) : a == CF && as_b == 0 , wcnt <= w1
//S1(001) : wait w1 , wcnt <= w2
//S2(011) : wait w2
//S3(111) : wait ~cpuclk , wcnt <= w3
//S4(110) : wait w3 
//S5(100) : wait as_b

digraph {
    //rankdir=LR;
    graph [ layout = dot ];
    node [shape = doublecircle]; S0 S1 S2 S3 S4 S5 ;
    node [shape = parallelogram]; CF_iocycシーケンス;
    node [shape = ellipse]; iosq;
    node [shape = rarrow]; cf_ce_n cf_iord_n cf_iowr_n dsack_n;
    CF_iocycシーケンス -> S0 [ label = "CF iocyc制御" ];
    S0 -> S1 [ label = "a == CF && as_b == 0 , wcnt <= w1" ];
    S1 -> S2 [ label = "wait w1 , wcnt <= w2"];
    S2 -> S3 [ label = "wait w2" ];
    S3 -> S4 [ label = "wait ~cpuclk , wcnt <= w3" ];
    S4 -> S5 [ label = "wait w3"];
    S5 -> S0 [ label = "wait as_b == 1" ];
    S1 -> cf_ce_n [ label = "Assert cf_ce_n" ];
    S5 -> cf_ce_n [ label = "Negate cf_ce_n" ];
    S2 -> cf_iord_n [ label = "cpurw=read,Assert cf_iord_n" ];
    S5 -> cf_iord_n [ label = "Negate cf_iord_n" ];
    S2 -> cf_iowr_n [ label = "cpurw=write,Assert cf_iowr_n" ];
    S4 -> cf_iowr_n [ label = "Negate cf_iowr_n" ];
    S3 -> dsack_n [ label = "Assert dsack_n"];
    S5 -> dsack_n [ label = "Negate dsack_n"];
    S0 -> S0 [ label = "wait iocyc start"];
    S1 -> S1  [ label = "wait w1 sysclk"];
    S2 -> S2  [ label = "wait w2 sysclk"];
    S3 -> S3  [ label = "wait cpuclk=0"];
    S4 -> S4  [ label = "wait w3 sysclk"];
    S5 -> S5  [ label = "wait as_b"];
    S0 -> iosq ;
    S5 -> iosq ;
}

 graphvizマジック?で3bit johnsonカウンタのステートダイヤグラムが何かごちゃごちゃやってる様に見える不思議。graphvizは読む側は知れぬが書く側には優しい。

今回はw1,w2,w3なる待ち時間を生成してそれを元に各タイミングを生成するのがお仕事になる。

3.コードに落とす為の設計メモ

 タイミングチャートからステートダイヤグラムを設計しCPLDのコードに落とす前に各ステートの細目とベースカウンタ(例によってジョンソンカウンタ)から生成する各タイミングを整理しておく。

// 2021/11/23 : CF用iosqを再整理、特にdsack

S0(000) : a == CF && as_b == 0 , wcnt <= w1
S1(001) : wait w1 , wcnt <= w2
S2(011) : wait w2
S3(111) : wait ~cpuclk , wcnt <= w3
S4(110) : wait w3 
S5(100) : wait as_b

L0 : S0                 S3
L1 :      S1                   S4
L2 :            S2                      S5

w1 : □---□
w2 :      □----□
w3 :                     □-----□

ce : ○---------------------------------○
rd :      ○----------------------------○
wr :      ○--------------------○
ak :                     ○-------------○

ce = L0 | L2
rd = L1 | L2
wr = L1
ak = !L0 & L2

 我流メモで意味不明な点はご容赦。

4.1 verilogコード(抜粋)

// CF bus cycle sequence
    always @( cpuclk or iosq or a or as_b ) begin
        case ( iosq )
            S0 : if ( as_b == 0 && a == cf ) begin
                    nwcnt <= 0 ; //4'd4 ;
                    niosq <= S1 ;
                end
                else
                    niosq <= S0 ;
            S1 : if ( wcnt != w1 ) begin
                     nwcnt <= wcnt + 1 ;
                     niosq <= S1 ;
                end
                else begin
                    nwcnt <= 0 ; 
                    niosq <= S2 ;
                end
            S2 : if ( wcnt != w2 ) begin
                    nwcnt <= wcnt + 1 ;
                    niosq <= S2 ;
                end
                else
                    niosq <= S3 ;
            S3 : if ( ~cpuclk )
                    niosq <= S3 ;
                else begin
                    niosq <= S4 ;
                    nwcnt <= 0 ;
                end
            S4 : if ( wcnt != w3 ) begin
                    nwcnt <= wcnt + 1 ;
                    niosq <= S4 ;
                end
                else
                    niosq <= S5 ;
            S5 : if ( as_b )
                    niosq <= S0 ;
                else
                    niosq <= S5 ;
        endcase
    end

    assign cf_ce_n = ~( ( iosq[0] | iosq[2] ) & ~as_b ) ;
    assign cf_iord_n = ~( ( iosq[1] | iosq[2] ) & ~as_b & cpurw ) ;
    assign cf_iowr_n = ~( iosq[1] & ~as_b & ~cpurw) ;

    assign dsack_n = ~((~iosq[0] & iosq[2]) ) ;

4.2 テストベンチ(抜粋)

task cf_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 ;
//    wait(~dsack_n);
#(STEP*2*7);
    as_b = 1 ; ds_b = 1 ;
#(STEP);
    cpurw = 1 ;
#(STEP*3);
end
endtask

4.3 シミュレーション結果(iverilog + gtkwave)

f:id:aki_iic:20211127164137p:plain

cf iosq simulation (iverilog + gtkwave)

・印刷形式

f:id:aki_iic:20211127180456p:plain

cf iosq simulation (BW)

 遅まきながら`timescale 1ns / 100psに設定したのでまともな時間軸になってる筈。

5.合成結果(或いは今回のオチ)

cpldfit:  version P.20131013                        Xilinx Inc.
                                  Fitter Report
Design Name: dramc                               Date: 11-24-2021, 11:30PM
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       
71 /108 ( 66%) 154 /540  ( 29%) 115/216 ( 53%)   33 /108 ( 31%) 46 /69  ( 67%)

 いつの間にやらマクロセル使用率が66%に増加している。前回の構成から25MC増加してるのは流石におかしいと色々調べてみたが原因判明せず。ベースカウンタを4ビットジョンソンにしてwcntカウンタを除去(FF数は減ってる筈)したら逆に75MCになってしまいこれがタイトルの所以である。もっとも今時の湯水の如くマクロセルなりロジックブロックを使えるデバイスからしたら108MCなぞ無いのと同じであろうが5V系のobsoluteなCPLD(且つIMTの世界)では今後の回路ブロック更新は慎重にならざるを得ない。。。

 

bus error例外と下手の考え休むに似たり

 迂遠を重ねて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でのシミュレーション波形

f:id:aki_iic:20211120074345p:plain

bus error

 berr_nのアサートタイミングがrefclkのエッジ(非同期)に依存するがそれは別途カウンタを設けても同じなので気にしない。。。

 (ジジイには)みづらいのでprintイメージの白黒画面も付けておく。

f:id:aki_iic:20211120075131p:plain

bus error timing (BW)

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から愚直にカウントしてみようと考えたり(マクロセル節約方針に反するので脳内却下したが)期待通りにシミュレーション出来なくてその原因を思いつかない場合に実態を確認せず思い込みで行動すると無為に時間と労力と気力を浪費してしまう教訓で最近の座右の銘ランク上昇中の言葉でもある。誰しも下手に埋没するのを潔しとせぬ故それに纏わる行為を努力と呼ぶのかもしれない(しみじみ)。

 

 

割り込み(その1)

 割り込み回路を組み込んでみる。とはいってもAutovectorを使うのでIRQ7〜IRQ1を74148風にプライオリティエンコードしてIPL_n[2:0]にエンコードしてCPUからのFC[2:0]=3'b111になったらas_bでも使ってavec_nをアサートする組み合わせ回路になる。

2.1 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のエッジ検出漏れ) 
// 2021/11/15 : 割り込みはautovector,irq7-0
 
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,irq_n,fc,avec_n,ipl_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
    input [7:1] irq_n ; // irq7-1
    input [2:0] fc ; // cpu function fc2-0

    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
    output avec_n ; // to cpu autovector
    output [2:0] ipl_n ; // to cpu interrupt request

    reg [2:0] ipl_n ; 
    reg [2:0] drsq,ndrsq ; // dram sequencer
    reg [3:0] rfsq,nrfsq ; // dram refresh sequencer
    reg cpuclk ; // sysclk/2

    wire dramcyc,rfshcyc ; // internal node (wire)

    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 ;
    parameter IRQ7=7'b0111111,IRQ6=7'b1011111,IRQ5=7'b1101111,IRQ4=7'b1110111,IRQ3=7'b1111011,IRQ2=7'b1111101,IRQ1=7'b1111110 ;
    parameter LVL7=3'b000,LVL6=3'b001,LVL5=3'b010,LVL4=3'b011,LVL3=3'b100,LVL2=3'b101,LVL1=3'b110 ;

// 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

    always @(irq_n) begin
        if (irq_n[7] == 1'b0) ipl_n = LVL7 ;
        else if (irq_n[6] == 1'b0) ipl_n = LVL6 ;
        else if (irq_n[5] == 1'b0) ipl_n = LVL5 ;
        else if (irq_n[4] == 1'b0) ipl_n = LVL4 ;
        else if (irq_n[3] == 1'b0) ipl_n = LVL3 ;
        else if (irq_n[2] == 1'b0) ipl_n = LVL2 ;
        else if (irq_n[1] == 1'b0) ipl_n = LVL1 ;
    end

    assign avec_n = ~((fc == 3'b111) & ~as_b) ;
    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

2.2 テストベンチ

// 2021/11/07 : 作業開始
// 2021/11/09 : テストベンチ
// 2021/11/13 : task文に書き換え
// 2021/11/14 : io関連のテストベンチ追加
// 2021/11/15 : 割り込み関連のテストベンチ追加

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
    reg [7:1] irq_n ; // interrupt request
    reg [2:0] fc ; // cpu function code

    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 ;
    wire [2:0] ipl_n ; // cpu ipl2-0
    wire avec_n ; // cpu autovector

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,irq_n,fc,avec_n,ipl_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 ;
    irq_n = 7'b1111111 ; fc = 3'b000 ;
#(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

task irq_cyc ;
    input [7:1] irq ;
begin
    irq_n = ~irq ;
#(STEP*2)
    fc = 3'b111 ;
    as_b = 0 ;
#(STEP*2*3)
    as_b = 1 ;
#(STEP*2*4)
    irq_n = 7'b1111111 ;
    fc = 3'b000 ;
#(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
    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);
    $display("シミュレーション終了");
    $finish ;
end

endmodule

3-4.シミュレーション波形(gtkwave)

f:id:aki_iic:20211116010059p:plain

interrupt priority encoder & autovector interrupt acknowledge cycle

 前回アップしたテキストにゴミが入っていたのが気になる(多分私がクリップボード経由で変な作業中データをぶち込んだ為?かもしれない)が今度はどうか。

5.合成結果

 
cpldfit:  version P.20131013                        Xilinx Inc.
                                  Fitter Report
Design Name: dramc                               Date: 11-16-2021,  0:50AM
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       
42 /108 ( 39%) 86  /540  ( 16%) 78 /216 ( 36%)   15 /108 ( 14%) 41 /69  ( 59%)

 プライオリティエンコーダとオートベクタ生成は組み合わせ回路そのもの故マクロセル4個の増加に収まっている(42/108 39%)。ピン使用率も41/69(59%)とIRQ7-1,FC[2:0],IPL[2:0]とインタフェース用にピンを消費している。割り込みマスク用レジスタはPSDのマクロセルかIOポートに割り振るつもりなのでCPLDはゲード回路だけを想定している。まだまだ何とも言えないが何度か述べているようにカウンタ外出しにすればマクロセル使用率80〜90%以下で収まりそうな可能性が出てきた(気がする)が妙な色気を出すと今迄の努力が台無しになりかねないので緊縮スタイル継続といふ事で。

 

*1:a == dram80)|(a == dram81

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

 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

ise14.7(2013.10.13版)

 シミュレーション出来たから合成してみた。今使ってるUbuntu16.04に導入したISE14.7(2013.10.13版)でXC95108PC84ターゲットで合成する。

f:id:aki_iic:20211113091404p:plain

ise14.7 dramc xc95108pc84

 途中 dramcyc,rfshcycが定義されていない、センシビリティリストに変なイベント(~rfshcycだった)と怒られたので適時修正。マクロセル使用率が:

*************************  Mapped Resource Summary  **************************

Macrocells     Product Terms    Function Block   Registers      Pins           
Used/Tot       Used/Tot         Inps Used/Tot    Used/Tot       Used/Tot       
32 /108 ( 30%) 63  /540  ( 12%) 35 /216 ( 16%)   15 /108 ( 14%) 13 /69  ( 19%)

** Function Block Resources **

Function    Mcells      FB Inps     Signals     Pterms      IO          
Block       Used/Tot    Used/Tot    Used        Used/Tot    Used/Tot    
FB1          11/18       18/36       18          17/90       1/12
FB2           2/18        1/36        1           1/90       2/12
FB3          16/18       12/36       12          42/90       2/12
FB4           1/18        1/36        1           1/90       1/11
FB5           1/18        2/36        2           1/90       1/11
FB6           1/18        1/36        1           1/90       1/11
             -----       -----                   -----       -----     
             32/108      35/216                  63/540      8/69 

* - Resource is exhausted

 今時点で30%(32/108)と微妙な状況にある。やはりリフレッシュクロックは外部(PIC12F1501辺りを想定)で生成してマクロセルを節約しないと(しても?)収まらない予感がする。もっともこの時代のCPLD(特にXC9500)は39入力有るのでFPGAと異なり積項を無駄に要するデコーダ系には適しているから大昔仕事で設計したXC95216PQ160の縮小版のノリで仕様・機能を最適化(減縮)しつつCPLDにフィッティングを目指す事にしよう。5Vの縛りが無ければ今時の使いやすく余裕のある(例えば秋月で変換基板込みで売ってるMACHXO2とか)が使えるしレベルシフタも検討したが駆動能力、設計のしやすさ、使い慣れている(obsoluteだけど)点からXC9500なるobsolute cpldでジタバタしてみたい。代替案として現役のMicrochip ATF1508(128MC)も入手してたりするのだがCADがCUPLでverilogは有料らしいので最後の手段に取っておく。。。