4.2.3 デュアルポートRAM

YACC プロジェクトで使用を予定しているので、動作を調べておきましょう。

次の記述をしてBlock RAMが一個使われることを確認したかったのですが、合成してみると使用数が2となってしまいます。(なにがいけなかったのでしょう?)

//Xilinx Block RAM
module ram8x512(clk,b_datain,a_addr,b_addr,we,a_out,b_out,write_address);
        parameter AWIDTH=9;
        parameter DWIDTH=8;
        input clk;
        input [DWIDTH-1:0] b_datain;
        input [AWIDTH-1:0] a_addr,b_addr,write_address;
        input   we;
        
        output [DWIDTH-1:0] a_out,b_out;
        reg [DWIDTH-1:0] mem [0: (1 << AWIDTH)-1];

        reg [AWIDTH-1:0] address_latch_a,address_latch_b;

        always @(posedge clk) begin
                address_latch_a <=a_addr;
        end

        always @(posedge clk) begin
                address_latch_b<=b_addr;
        end

        assign a_out=mem[address_latch_a];
        assign b_out=mem[address_latch_b];

        always @(posedge clk) begin
                mem[write_address] <=b_datain;
        end
endmodule 

仕方ないのでUNISIMフォルダにあるRAMB4_S8_S8を使うことにしました。512Wordsx8bitです。これで動作を確認しておきましょう。


合成対象ソース

前章のled.vを流用します。
RAMに counterをアドレスとしてcounterの値を書きます。読み出しは、counter アドレスと ~counterアドレスで読み出します。

//-------------------------------------------------------------------
//
//   This file is automatically generated by VHDL to Verilog Translator.
//         Ver.1.08 Build Mar.6.2004
//               www.sugawara-systems.com   
//                    tech-support@sugawara-systems.com
//        See Original Copyright Notice for property of the file .( somewhere in this file.)
//
//--------------------------------------------------------------------


//--------------------------------------------
// NP1003 SDRAM & USB template source file
// (C) Copyright 2003 Nahitafu Nahitech
// http://www.nahitech.com
//--------------------------------------------

`timescale 1ns/1ns
`define ns 1
`define us (1000*`ns)
`define ms (1000*`us)
`define sec (1000*`ms)

module  main ( clk0, clk1, clk2, clk3, user, sd_d, sd_a, sd_bs1, sd_bs0, sd_ldqm, sd_udqm, sd_wen, sd_ras, sd_cas, sd_clk, sd_cke, sd_cs, usb_d, usb_wr, usb_rd, usb_txe, usb_rxf, usb_pwren, usb_rsto );
    
    input clk0;
    input clk1;
    input clk2;
    input clk3;
    inout [36:0]  user ;
    inout [15:0]  sd_d ;
    output [12:0]  sd_a ;
    output sd_bs1;
    output sd_bs0;
    output sd_ldqm;
    output sd_udqm;
    output sd_wen;
    output sd_ras;
    output sd_cas;
    output sd_clk;
    output sd_cke;
    output sd_cs;
    inout [7:0]  usb_d ;
    output usb_wr;
    output usb_rd;
    input usb_txe;
    input usb_rxf;
    input usb_pwren;
    input usb_rsto;


    wire clk;
    wire logich;
    wire logicl;
    integer usb_state;
    wire usb_rxf_node;
    wire usb_txe_node;
    wire usb_rx_ready;
    wire usb_tx_ready;
    wire usb_received;
    wire usb_transmitted;
    wire [7:0] usb_tx_data;
    wire [7:0] usb_rx_data;
    wire usb_wr_node;
    wire usb_rd_node;
    reg /* conflict */[36:0] user_;//TAK Jun.12.2004 =37'bz;
    wire /* conflict */[36:0] user;
    wire [15:0] sd_d;
    wire [12:0] sd_a;
    wire sd_bs1;
    wire sd_bs0;
    wire sd_ldqm;
    wire sd_udqm;
    wire sd_wen;
    wire sd_ras;
    wire sd_cas;
    wire sd_clk;
    wire sd_cke;
    wire sd_cs;
    wire [7:0] usb_d;
    wire usb_wr;
    wire usb_rd;
    reg [25:0] counter; 
        wire [7:0] a_out,b_out;
    
   assign {clk}=clk0;
   
   assign {logich}=1'b1;
   
   assign {logicl}=1'b0;
   
   assign {sd_clk}=~ (clk);
   
   assign {sd_cs}=1'b1;
   
   assign {sd_ras}=1'b1;
   
   assign {sd_cas}=1'b1;
   
   assign {sd_cke}=1'b0;
   
   assign {sd_wen}=1'b1;
   
   assign {sd_d}={(15-0+1- 0){1'bz}};
   
   assign {sd_a}={(12-0+1- 0){1'b0}};
   
   assign {sd_bs1}=1'b0;
   
   assign {sd_bs0}=1'b0;
   
   assign {sd_ldqm}=1'b0;
   
   assign {sd_udqm}=1'b0;
   
   assign {usb_rd_node}=logich;
   
   assign {usb_wr_node}=logicl;
   
   assign {usb_rd}=usb_rd_node;
   
   assign {usb_wr}=usb_wr_node;
   
   assign {usb_d}={(7-0+1- 0){1'bz}};
   
 // TAK Jun.12.2004 assign {user[35:0]}={(35-0+1- 0){1'b0}};
   

   always @(posedge clk) begin
        if (!usb_rsto) counter <=0;
        else counter <=counter+1;
   end

       
   always @ (posedge clk ) begin
               user_[36] <= (usb_txe & usb_rxf);
               
           
   end //always
//   assign user = user_;//Added for Conflict reslover.
    assign user[3:0]=counter[25:22];
    assign user[36:4]={a_out,b_out,counter[16:0]};  



 //ram8x512  ram0(.clk(clk),.b_datain(counter[7:0]),.a_addr(counter[8:0]),.b_addr(counter[9:1]),.we(1'b1),.a_out(a_out),.b_out(b_out),.write_address(counter[8:0]));

 RAMB4_S8_S8 ram0(.DOA(a_out), .DOB(b_out), .ADDRA(counter[8:0]), .ADDRB(~counter[8:0]), .CLKA(clk), .CLKB(clk), .DIA(counter[7:0]), .DIB(counter[7:0]), .ENA(1'b1), .ENB(1'b1), .RSTA(1'b0), .RSTB(1'b0), .WEA(1'b0), .WEB(1'b1));

endmodule


テストベンチ

前章のLEDプロジェクトを流用します。RTLでは、`define RTLをイネーブルしVCDに結果を書き込みます。

`timescale 1ns/1ps
`define CYCLE  (10) //(6.522/2)
`define RTL
module led_test;


    reg  clk0=0;
    reg clk1;
    reg clk2;
    reg clk3;
    wire [36:0]  user ;
    wire [15:0]  sd_d ;
    wire [12:0]  sd_a ;
    wire sd_bs1;
    wire sd_bs0;
    wire sd_ldqm;
    wire sd_udqm;
    wire sd_wen;
    wire sd_ras;
    wire sd_cas;
    wire sd_clk;
    wire sd_cke;
    wire sd_cs;
    wire [7:0]  usb_d ;
    wire usb_wr;
    wire usb_rd;
    reg usb_txe=0;
    reg usb_rxf=0;
    reg usb_pwren=0;
    reg usb_rsto=0;
    wire [16:0] counter;
    wire [7:0] a_out,b_out;

  main  led( .clk0(clk0), .clk1(clk1), .clk2(clk2), .clk3(clk3), .user(user), .sd_d(sd_d),
        .sd_a(sd_a), .sd_bs1(sd_bs1), .sd_bs0(sd_bs0), .sd_ldqm(sd_ldqm), .sd_udqm(sd_udqm),
        .sd_wen(sd_wen), .sd_ras(sd_ras), .sd_cas(sd_cas), .sd_clk(sd_clk), .sd_cke(sd_cke),
        .sd_cs(sd_cs), .usb_d(usb_d), .usb_wr(usb_wr), .usb_rd(usb_rd),
        .usb_txe(usb_txe), .usb_rxf(usb_rxf), .usb_pwren(usb_pwren), .usb_rsto(usb_rsto) );
    

   always #(`CYCLE) clk0=~clk0;

   assign a_out=user[36:29];
   assign b_out=user[28:21];
   assign counter=user[20:4];
   initial begin
        `ifdef RTL
                    $dumpfile("led_rtl.vcd");
                    $dumpvars(0, led_test);
        `endif
                #150;
                @(negedge clk0);
                usb_rsto=1;
                #100000;
                $finish;

   end






endmodule

RTLプロジェクト

前章と違うのは、UNISIMのソースを使ったのでそれを追加しています。

遅延シミュレーション

プロジェクトは、前章の遅延シミュレーションと同じです。



前章と同じように合成します。led_test.vだけ`define RTLをコメントアウトして走らせます。
下図は、遅延シミュレーション波形です。最初のクロックエッジで発生しているタイミングエラーは、シミュレータの電源Onからの過渡状態
によるものですから無視してください。
RTLシミュレーションで、VCDで保存した波形が 青い文字になっています。波形を見ればお分かりの通り、RTLシミュレーションでは、時間0
の遅延ですから、クロックの立ち上がりエッジで変化しています。一方遅延シミュレーションでは、クロックエッジから遅れている様子が分かります。counter は、約10ns、RAMのRead出力であるa_out,b_outは、counter出力よりもさらに遅くカーソル間では、15.293nsかかっている様子が分かります。このようにRTLと比較することで、合成結果の妥当性を簡単に検証することができませす。ちなみに、Xilinxの遅延シミュレーションではRAMが入っていても、Alteraと違ってかなり快適に走るようです。(ネットリストの生成方法がまったく違うことに起因します。)

残念ながら、RAMの動作としては、AlteraとXilinxでは違うようです。Read出力に関してAddress Regで一旦受けるのは変わらないのですが、Write入力に関しては、Xilinxでは、Address Regで受けていないようです。従って、XilinxのRAMはRegisterファイルとしてもなんら問題ありません。