Interfacing external RAM to a Xilinx FPGA using Verilog

Topics relating to PALs, CPLDs, FPGAs, and other PLDs used for the support or creation of 65-family processors, both hardware and HDL.
ElEctric_EyE
Posts: 3260
Joined: 02 Mar 2009
Location: OH, USA

Interfacing external RAM to a Xilinx FPGA using Verilog

Post by ElEctric_EyE »

Hello Verilog experts. This is a question involving Verilog HDL and a Xilinx Spartan 6 in order to interface external memory with a single bi-directional databus to an FPGAs dual uni-directional data buses.

In digital systems inside of an FPGA particularly embedded CPU's there are separate databus in and databus out signals. In the real world we still have asynchronous and synchronous RAMs that are bi-directional. How to interface these 2 systems is what I am questioning, since I've limited understanding of Verilog and have seen some complicated designs.

I do know, on new FPGA's, that a bi-directional interface is done only on the top_level of an HDL or even a schematic design. Also, when using Verilog, the term 'inout' is used to spec a bi-directional bus.

What I don't know is about where to start for the simplest of interfaces to an external Synchronous RAM where one has a clock, CS, WE, data bus, & address bus.

EDIT: 2/18/2013 Changed title. Clarified question.
Last edited by ElEctric_EyE on Tue Feb 19, 2013 3:09 am, edited 1 time in total.
User avatar
MichaelM
Posts: 761
Joined: 23 Apr 2012
Location: Huntsville, AL

Re: How to interface real world RAM to a FPGA CPU using Veri

Post by MichaelM »

In the code fragment below is the definition for an asynchronous external SRAM interface for a product I built several years ago.

Code: Select all

    //  Image Memory (Dual Asynchronous 512k x 16 RAM) Interface

    output  [ 1:0] nRCE,    // SRAM Chip Enable: 0 - PH #0, 1 - PH #1
    output  nROE,           // SRAM Output Enable
    output  nRWE,           // SRAM Write Enable
    output  [ 1:0] nRBE,    // SRAM Byte (Write) Enable

    output  [18:0] RA,      // SRAM Address
    inout   [15:0] RD,      // SRAM Data Bus
I designed and implemented a module to arbitrate access to the external SRAM by several other modules. The following code fragment demonstrates how the connections from the top level ports are made to the internal SRAM interface control module.

Code: Select all

////////////////////////////////////////////////////////////////////////////////
//
//	Instantiate External Memory Interface
//

MPHC_MemIF  RAM (
                .Rst(Rst), 
                .Clk(Clk), 

                .RCE(RCE), 
                .ROE(ROE), 
                .RWE(RWE), 
                .RBE(RBE), 
                .RA(RA), 
                .RD(RD),

                .Rqst(Rqst), 
                .Grnt(Grnt),

                .MemIF_WE(MemIF_WE), 
                .MemIF_Cmd(MemIF_Cmd), 
                .MemIF_AO(MemIF_AO), 
                .MemIF_DO(MemIF_DO), 
                .MemIF_FF(MemIF_FF), 
                .MemIF_EF(MemIF_EF), 
                .MemIF_DI_Valid(MemIF_DI_Valid), 
                .MemIF_DI(MemIF_DI),

                .SM(MemIF_SM)
            );

assign nRCE = ~RCE;
assign nROE = ~ROE;
assign nRWE = ~RWE;
assign nRBE = ~RBE;
The "wires" declared in the top level port list are simply connected to the SRAM interface module. My convention is to use only active high signals, i.e. no mixed logic, within the FPGA. This is why the active low outputs to the external SRAM are "assigned" through inverters at the top most level.

The following code fragment shows the definitions for the SRAM interface module ports:

Code: Select all

module MPHC_MemIF(
    input   Rst,
    input   Clk,
    
    //  MPHC_MemIF External SRAM Interface Signals
    
    output  reg [1:0] RCE,
    output  reg ROE,
    output  reg RWE,
    output  reg [ 1:0] RBE,
    output  reg [18:0] RA,
    inout   [15:0] RD,
    
    //  MPHC_MemIF Request/Grant Arbiter Interface
    
    input   [2:0] Rqst,         // Memory Interface Request: (hold until done)
                                //     2 - PH[0],
                                //     1 - PH[1],
                                //     0 - External Controller (Computer)
    output  reg [2:0] Grnt,     // Memory Interface Grant (Acknowledge)

    //  MPHC_MemIF Client Interface Signals
    
    inout   MemIF_WE,           // Memory Interface Write Enable
    inout   MemIF_Cmd,          // Memory Interface Cmd (0 - Read; 1 - Write)
    inout   [19:0] MemIF_AO,    // Memory Interface Address FIFO
    inout   [15:0] MemIF_DO,    // Memory Interface Data Out FIFO
    
    output  MemIF_FF,           // Memory Interface FIFO Full Flag
    output  MemIF_EF,           // Memory Interface FIFO Empty Flag
    
    output  reg [2:0] MemIF_DI_Valid,   // Memory Interface Data Input Valid
    output  [15:0] MemIF_DI,    // Memory Interface Data Input

    //  MPHC_MemIF State Machine
    
    output  reg [1:0] SM        // Transaction processing state machine
);
The bidirectional definition of the SRAM Data bus, RD, is carried into the module. This is not necessary, but for this implementation it was acceptable. The FPGA does not actually have a bidirectional bus. In other words, only on the IO pin side of the IO block (IOB) does a "bidirectional" signal exist. Coming into the IOB from the FPGA logic fabric, and going into the FPGA logic fabric from the IOB, the signals are unidirectional like every where else in the FPGA.

Thus within the SRAM interface module, I have an SRAM Data Output bus and an SRAM Data Input bus. It is these busses that are connected to the "bidirectional" FPGA pins. The use of separate signal names allows the synthesizer to determine the input/output connections to the input and output buffers/registers of the IOB. Since I generally synthesize with the pack into IOB option on, the synthesizer and mapper attempt to place these I/O components into the IOB itself. (I check the reports to see that the number of IOB registers matches my expectations for the design. I also look for warnings that indicate that some signals have not been merged into IOBs as I expect.)

The following code fragment shows how the connections of the internal signals are made to the "bidirectional" bus so that the appropriate input and output registers/buffers are inferred and placed in the IOB:

Code: Select all

//  Generate the RAM output buffer enable

assign RDO_OE = |SM & CurCmd;

//  Drive the RAM Bidirectional Data Bus with RDO when RDO_OE asserted

assign RD = ((RDO_OE) ? RDO : 16'bZ);

//  Capture the RAM Bidirectional Data Bus during read cycles using the delay
//  line for generating MemIF_DI_Valid to the clients

assign CE_RDI = |(RdCycle[1]);

always @(posedge Clk)
begin
    if(Rst)
        RDI <= #1 0;
    else if(CE_RDI)
        RDI <= #1 RD;
end
The assign RD statement demonstrates that at least a tri-state output buffer is expected to be inferred. The RDO_OE signal controls the output enable of the tri-state output buffer in the IOB. The CE_RDI signal creates a clock enable with which to sample the asynchronous output data from the SRAM. Notice that the "inout" signal, RD, appears both on the left and on the right of the assignment statements. Because of this, the synthesizer infers the RDI register and "places" it in the IOB as a registered input.
Michael A.
User avatar
Arlet
Posts: 2353
Joined: 16 Nov 2010
Location: Gouda, The Netherlands
Contact:

Re: How to interface real world RAM to a FPGA CPU using Veri

Post by Arlet »

Here is an SRAM controller I made. It's been a while since I used this, so I'm not 100% sure it was in a working state when I left it. I used it on a Spartan-3 eval kit, that had 2x16 bit SRAMs on it. Here is the inout bit you were interested in:

Code: Select all

inout  [15:0] ram_d1,
inout  [15:0] ram_d2,

...

assign ram_d1 = ram_we ? 16'bZ : ram_d; 
assign ram_d2 = ram_we ? 16'bZ : ram_d;
ElEctric_EyE
Posts: 3260
Joined: 02 Mar 2009
Location: OH, USA

Re: Interfacing external RAM to a Xilinx FPGA using Verilog

Post by ElEctric_EyE »

Guys, thanks for your inputs. I've read them many times this past week as I make additions.
Arlet wrote:
assign ram_d1 = ram_we ? 16'bZ : ram_d;
Am I correct in seeing that ram_d will be output through FPGA to the external RAM when ram_we is logic '0' for a write-to-ram?
More importantly when ram_we is logic '1' the FPGA output is logic 'Z' and the data from the external RAM will be present on ram_d1?

EDIT: I see this is the case with your last statement. I had missed that before I posted:

Code: Select all

always @(posedge clk)
	read_data <= bank_1 ? ram_d2 : ram_d1;
So it is a little confusing, but I think I understand now.
User avatar
Arlet
Posts: 2353
Joined: 16 Nov 2010
Location: Gouda, The Netherlands
Contact:

Re: Interfacing external RAM to a Xilinx FPGA using Verilog

Post by Arlet »

Correct.
ElEctric_EyE
Posts: 3260
Joined: 02 Mar 2009
Location: OH, USA

Re: Interfacing external RAM to a Xilinx FPGA using Verilog

Post by ElEctric_EyE »

I think I have something that makes sense and looks good in RTL also. Is there anything that stands out as absolutely wrong?

Code: Select all

`timescale 1ns / 1ps
															//Note: External SyncRAM is used for videoram
module top_level( input MAINCLK1,				//100MHz input from onboard can oscillator
						input cpuRST,					//active high Reset for 65Org16 core
					   inout  [15:0] SRD,			//from/to SyncRAM databus
					   output [31:0] cpuAB,			//cpu address bus
						output [20:0] SRaddr,		//SyncRAM address bus
					   output SRWEn,					//SyncRAM Write Enable, active low
						output reg SRCSn = 0,		//SyncRAM Chip Select always selected
					   output CLKout,					//main 70MHz clock to offboard ICs like videoDAC and syncRAM
						output vsync,					//to CRT
						output hsync					//to CRT
					   );

reg [15:0] SRDO;									//reg output goes to ORs module. ORs module output goes to cpu data in
								
always @(posedge clk) begin
	if (vramCS) begin 							//
		if (!cpuWE)
			SRDO <= SRD;							//if reading from SyncRAM, put data into reg
	end
	else 
		SRDO <= 16'h0000;							//else reg will output zeroes when vram is not selected
end

wire [20:0] Vaddr;								//video address from HVSync generator

assign SRWEn = !(cpuWE & vramCS);			//SyncRAM write enable, active low during a write to video memory by cpu
assign SRaddr = vramCS ? cpuAB :	Vaddr;	//MUX the SyncRAM address for video timing & cpu access
assign SRD = SRWEn ? 16'hZZZZ : cpuDO;		//Output port to SyncRAM databus. High 'Z' during a read
................
................
ElEctric_EyE
Posts: 3260
Joined: 02 Mar 2009
Location: OH, USA

Re: Interfacing external RAM to a Xilinx FPGA using Verilog

Post by ElEctric_EyE »

Simulation is running too. I will add the RGB outputs for the videoDAC and load the project to the FPGA and see what I get. :|
User avatar
Arlet
Posts: 2353
Joined: 16 Nov 2010
Location: Gouda, The Netherlands
Contact:

Re: Interfacing external RAM to a Xilinx FPGA using Verilog

Post by Arlet »

So, what happens when the CPU accesses the memory ? Just show black dots ?
ElEctric_EyE
Posts: 3260
Joined: 02 Mar 2009
Location: OH, USA

Re: Interfacing external RAM to a Xilinx FPGA using Verilog

Post by ElEctric_EyE »

I thought this piece of code would output the cpu data?

Code: Select all

assign SRD = SRWEn ? 16'hZZZZ : cpuDO;		//Output port to SyncRAM databus. High 'Z' during a read	
EDIT: or I need something like this?

Code: Select all

reg [15:0] SRDO;									//reg output goes to ORs module. ORs module output goes to cpu data in
reg [15:0] SRDI;									//reg output goes to SyncRAM databus
								
always @(posedge clk) begin
	if (vramCS) begin 							//
		if (!cpuWE)
			SRDO <= SRD;							//when reading from SyncRAM, put data into reg
			else
				SRDI <= cpuDO;						//when writing to SyncRAM, output data from cpu to reg
	end
	else 
		SRDO <= 16'h0000;							//else reg will output zeroes when vram is not selected
end

wire [20:0] Vaddr;								//video address from HVSync generator

assign SRWEn = !(cpuWE & vramCS);			//SyncRAM write enable, active low during a write to video memory by cpu
assign SRaddr = vramCS ? cpuAB :	Vaddr;	//MUX the SyncRAM address for video timing & cpu access
assign SRD = SRWEn ? 16'hZZZZ : SRDI;		//Output port to SyncRAM databus. High 'Z' during a read	
User avatar
Arlet
Posts: 2353
Joined: 16 Nov 2010
Location: Gouda, The Netherlands
Contact:

Re: Interfacing external RAM to a Xilinx FPGA using Verilog

Post by Arlet »

I was talking about this line:

Code: Select all

SRDO <= 16'h0000;                     //else reg will output zeroes when vram is not selected
ElEctric_EyE
Posts: 3260
Joined: 02 Mar 2009
Location: OH, USA

Re: Interfacing external RAM to a Xilinx FPGA using Verilog

Post by ElEctric_EyE »

Isn't it implied that after the second 'else', that it means?

Code: Select all

else if (!vramCS) 
     SRDO <= 16'h0000;
since I need to output zeroes into the cpu when the videoram is not selected.
ElEctric_EyE
Posts: 3260
Joined: 02 Mar 2009
Location: OH, USA

Re: Interfacing external RAM to a Xilinx FPGA using Verilog

Post by ElEctric_EyE »

Aha! I see your point now. I was getting good sync signals as the monitor LED was green but black screen. Then I realized I needed not only a read databus from the SyncRAM to the cpu, but also another read databus from the SyncRAM to the videoDAC.
After some real world testing I'm getting video output, seemingly random pixels being read in from the RAM. The cpu isn't running as I don't have a reset button or circuit yet in this old version of the PVB. I'll wire something up real quick so I can try writing to the RAM from the cpu.

Code: Select all

reg [15:0] SRDO;									//reg output goes to ORs module. ORs module output goes to cpu data in
reg [15:0] VDAC;									//reg output goes to HVSync module then to videoDAC
								
always @(posedge clk) begin
	VDAC <= SRD;									//always send SyncRAM data to videoDAC
	if (vramCS) begin 							//
		if (!cpuWE)
			SRDO <= SRD;							//when reading from SyncRAM, put data into reg
	end
	else 
		SRDO <= 16'h0000;							//else reg will output zeroes when vram is not selected
end

wire [20:0] Vaddr;								//video address from HVSync generator

assign SRWEn = !(cpuWE & vramCS);			//SyncRAM write enable, active low during a write to video memory by cpu
assign SRaddr = vramCS ? cpuAB :	Vaddr;	//MUX the SyncRAM address for video timing & cpu access
assign SRD = SRWEn ? 16'hZZZZ : cpuDO;		//Output port to SyncRAM databus. High 'Z' during a read	
EDIT: I realize I can just make a simple reset circuit inside the FPGA, but outside the cpu! :roll:
ElEctric_EyE
Posts: 3260
Joined: 02 Mar 2009
Location: OH, USA

Re: Interfacing external RAM to a Xilinx FPGA using Verilog

Post by ElEctric_EyE »

Oh wow, it seems to be working! :lol: :lol:

I wrote the first 65K worth of pixels blue in a endless loop and it seems to have done it correctly.
User avatar
Arlet
Posts: 2353
Joined: 16 Nov 2010
Location: Gouda, The Netherlands
Contact:

Re: Interfacing external RAM to a Xilinx FPGA using Verilog

Post by Arlet »

If they're all blue, how do you know if it's working correctly ? :P
ElEctric_EyE
Posts: 3260
Joined: 02 Mar 2009
Location: OH, USA

Re: Interfacing external RAM to a Xilinx FPGA using Verilog

Post by ElEctric_EyE »

Because now with the following program:

Code: Select all

START:	    LDA #$0000
            TAZP                 ;SET ZEROPAGE @$0000_0000
            LDA #$0001
            TASP                 ;SET STACKPAGE @$0001_0000
            LDA #$0000
            STA $00000000
            LDA #$8000
            STA $00000001
            LDA #$0000
            LDY #$0000
            LDX #$000C
AA          STA ($0000),Y        ;$8000_0000 begin videoRAM
            DEY
            BNE AA
            INC $00000001
            DEX
            BNE AA
AB          JMP AB
I get the following with timing issues:
Attachments
P1000154.JPG
Post Reply