Interfacing external RAM to a Xilinx FPGA using Verilog
-
ElEctric_EyE
- Posts: 3260
- Joined: 02 Mar 2009
- Location: OH, USA
Interfacing external RAM to a Xilinx FPGA using Verilog
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.
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.
Re: How to interface real world RAM to a FPGA CPU using Veri
In the code fragment below is the definition for an asynchronous external SRAM interface for a product I built several years ago.
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.
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:
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:
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.
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
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 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
);
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
Michael A.
Re: How to interface real world RAM to a FPGA CPU using Veri
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
Guys, thanks for your inputs. I've read them many times this past week as I make additions.
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:
So it is a little confusing, but I think I understand now.
Arlet wrote:
assign ram_d1 = ram_we ? 16'bZ : ram_d;
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;-
ElEctric_EyE
- Posts: 3260
- Joined: 02 Mar 2009
- Location: OH, USA
Re: Interfacing external RAM to a Xilinx FPGA using Verilog
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
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. 
Re: Interfacing external RAM to a Xilinx FPGA using Verilog
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
I thought this piece of code would output the cpu data?
EDIT: or I need something like this?
Code: Select all
assign SRD = SRWEn ? 16'hZZZZ : cpuDO; //Output port to SyncRAM databus. High 'Z' during a read 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 Re: Interfacing external RAM to a Xilinx FPGA using Verilog
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
Isn't it implied that after the second 'else', that it means? since I need to output zeroes into the cpu when the videoram is not selected.
Code: Select all
else if (!vramCS)
SRDO <= 16'h0000;-
ElEctric_EyE
- Posts: 3260
- Joined: 02 Mar 2009
- Location: OH, USA
Re: Interfacing external RAM to a Xilinx FPGA using Verilog
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.
EDIT: I realize I can just make a simple reset circuit inside the FPGA, but outside the cpu! 
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 -
ElEctric_EyE
- Posts: 3260
- Joined: 02 Mar 2009
- Location: OH, USA
Re: Interfacing external RAM to a Xilinx FPGA using Verilog
Oh wow, it seems to be working!
I wrote the first 65K worth of pixels blue in a endless loop and it seems to have done it correctly.
I wrote the first 65K worth of pixels blue in a endless loop and it seems to have done it correctly.
Re: Interfacing external RAM to a Xilinx FPGA using Verilog
If they're all blue, how do you know if it's working correctly ? 
-
ElEctric_EyE
- Posts: 3260
- Joined: 02 Mar 2009
- Location: OH, USA
Re: Interfacing external RAM to a Xilinx FPGA using Verilog
Because now with the following program:
I get the following with timing issues:
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