割り込み(その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