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によるシミュレーション波形
ポートサイズを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]