Xylo-LM SDRAM Block I/O and 2 Port

Saxo/-L & Xylo/-EM/-L/-LM boards

Xylo-LM SDRAM Block I/O and 2 Port

Postby reedb » Mon Apr 06, 2009 1:41 pm

I'm interested in block reads and writes (buffer length > 1 WORD) to the SDRAM. Has any one made any progress on this that they would be willing to share?

Same question for adding a second port to the SDRAM (even if it's just unidirectional: PC->FX2->SDRAM->FPGA-FIFO).

I'll be working on these issues for the next couple of weeks and would also be interested in collaboration.
reedb
 
Posts: 14
Joined: Sun Apr 05, 2009 4:06 pm
Location: Bowen Island, Canada

My simple solution, host side

Postby reedb » Mon Jun 29, 2009 3:08 pm

I used the following code on the host side to implement SDRAM block reads and writes. It implements the following interface changes:

1. No read turnaround cycle on writes.
2. Change USB packet layout to put data at end of packet.
3. Make USB data payload a DWORD.

These changes give a factor of four speed up on block writes to the SDRAM. Just add the following code to FX2_SDRAM.EzUSB.cpp and change SDRAM_Xylo-LM.v to match:

// Interface command definitions. Used by host software to drive the interface.
//
#define CMD_WRITE 0x00
#define CMD_READ 0x01
#define CMD_PRECHARGE 0x02
#define CMD_LOADMODE 0x06

// USB packet definition
//
#pragma pack(push, 1)

typedef struct tagUSB_PACKET
{
BYTE bAdd[3]; // 24 bits of address
BYTE bCmd; // Interface command
BYTE bDat[4]; // Payload data
} USB_PACKET, *PUSB_PACKET;

#pragma pack(pop)

// SDRAM initialization constants. See MT48LC4M16A2 data sheet for details.
//
#define BMODE_BURST_LEN1 0x20 // Burst length one WORD, CAS Latency = 2, Sequential burst
#define BMODE_BURST_LEN2 0x21 // Burst length two WORD's, CAS Latency = 2, Sequential burst
#define BMODE_BURST_LEN4 0x22 // Burst length four WORD's, CAS Latency = 2, Sequential burst
#define BMODE_BURST_LEN8 0x23 // Burst length eight WORD's, CAS Latency = 2, Sequential burst
#define BMODE_BURST_LEN256 0x22 // Burst length full page, 256 WORD's, CAS Latency = 2, Sequential burst

#define PRECHARGE_ALL 0x04 // A10 HIGH: All banks precharged and BA0, BA1 are Don’t Care.

//*****************************************************************************
//
// InitSDRAM - Initialize SDRAM. See MT48LC4M16A2 data sheet (www.micron.com/sdram)
// "Mode Register Definition" for mode definition details.
//
// Paramaters
// bMode - Will be applied to A0-A7 during mode initialization.
//
// Returns - Nothing.
//
//*****************************************************************************

void InitSDRAM(BYTE bMode)
{
static USB_PACKET usbpPrecharge = {0x00, 0x00, PRECHARGE_ALL, CMD_PRECHARGE, 0x00, 0x00, 0x00, 0x00};

USB_BulkWrite(2, &usbpPrecharge, sizeof(usbpPrecharge));

static USB_PACKET usbpLoadModeReg = { 0x00, 0x00, 0x00, CMD_LOADMODE, 0x00, 0x00, 0x00, 0x00};

usbpLoadModeReg.bAdd[1] = bMode;

USB_BulkWrite(2, &usbpLoadModeReg, sizeof(usbpLoadModeReg));
}

//*****************************************************************************
//
// WriteBlockSDRAM - Write a block of memory to SDRAM.
//
// Paramaters
// dwAdd - SDRAM Address, 24 bits, destination.
// dwLen - Length of block to write in bytes.
// pb - Pointer to source.
//
// Returns - Nothing.
//
//*****************************************************************************

void WriteBlockSDRAM(DWORD dwAdd, DWORD dwLen, PBYTE pb)
{
assert(dwAdd < 4194304);
assert(dwLen < 4194304);
assert((dwAdd + dwLen) < 4194304); // 4 MByte SDRAM

assert(!(dwAdd & 1)); // Address must be even (WORD access only)
assert(!(dwLen & 1)); // Length must be even (WORD access only)

static USB_PACKET usbpWrite;

// Switch to DWORD transfer for block writes.
//
InitSDRAM(0x21);

for (DWORD i = 0; i < dwLen; i+= 4) {

*((PDWORD)&usbpWrite.bAdd) = dwAdd;
usbpWrite.bCmd = CMD_WRITE; // Overwrite MSByte of address with command.

*((PDWORD)&usbpWrite.bDat[0]) = HIWORD(*(PDWORD)pb);
*((PDWORD)&usbpWrite.bDat[2]) = LOWORD(*(PDWORD)pb);

usbpWrite.bCmd = 0;

DWORD nBytes;

static DWORD pipe = 2;

DeviceIoControl(XyloDeviceHandle,
IOCTL_EZUSB_BULK_WRITE,
&pipe,
sizeof(pipe),
&usbpWrite,
sizeof(usbpWrite),
&nBytes,
NULL);

assert(nBytes == sizeof(usbpWrite));

pb+= 4;
dwAdd+= 2;
}

// Switch to back to WORD transfer.
//
InitSDRAM(0x20);
}

//*****************************************************************************
//
// ReadBlockSDRAM - Read a block of memory to SDRAM.
//
// Paramaters
// dwAdd - SDRAM Address, 24 bits, source.
// dwLen - Length of block to read in bytes.
// pb - Pointer to destination.
//
// Returns - Nothing.
//
//*****************************************************************************

void ReadBlockSDRAM(DWORD dwAdd, DWORD dwLen, PBYTE pb)
{
assert(dwAdd < 4194304);
assert(dwLen < 4194304);
assert((dwAdd + dwLen) < 4194304); // 4 MByte SDRAM

assert(!(dwAdd & 1)); // Address must be even (WORD access only)
assert(!(dwLen & 1)); // Length must be even (WORD access only)

// Use WORD transfer for reads.
//
InitSDRAM(0x20);

static USB_PACKET usbpRead = { 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00};

for (DWORD i = 0; i < dwLen; i+= 2) {

WORD bufr;

*((PDWORD)&usbpRead.bAdd) = dwAdd;

usbpRead.bCmd = CMD_READ; // Overwrite MSByte of address with command.

USB_BulkWrite(2, &usbpRead, sizeof(usbpRead));
USB_BulkRead(4, &bufr, sizeof(bufr));

*(PWORD)pb = bufr;
pb+= 2;
dwAdd++;
}
}
reedb
 
Posts: 14
Joined: Sun Apr 05, 2009 4:06 pm
Location: Bowen Island, Canada

Postby reedb » Mon Jun 29, 2009 3:12 pm

I used the following code on the FPGA side to implement SDRAM block reads and writes. It implements the following interface changes:

1. No read turnaround cycle on writes.
2. Change USB packet layout to put data at end of packet.
3. Make USB data payload a DWORD.

These changes give a factor of four speed up on block writes to the SDRAM. Just add the following code to SDRAM_Xylo-LM.v and change FX2_SDRAM.EzUSB.cpp to match:

[size=9]module xylo_sdram3(
FX2_CLK, // FIFO clock
FX2_FD, // FIFO bidirectional data
FX2_SLRD, // FIFO read strobe
FX2_SLWR, // FIFO write strobe
FX2_FLAGS, // FIFO flags
FX2_PA_2, // FPGA wants the FX2 to drive the DATA bus
FX2_PA_3, // FX2 Enable
FX2_PA_4, // FIFO_ADR lsb
FX2_PA_5, // FIFO_ADR msb
FX2_PA_6, // End of packet
FX2_PA_7, // FIFO5 ready to accept data

SDRAM_CLK, // Clock. All SDRAM input signals are sampled on the positive edge of CLK.
SDRAM_CKE, // Clock enable. Always enabled.
SDRAM_CMD, // Command input.
SDRAM_DQM, // Data bus (DQ) enable. Active low.
SDRAM_BA, // Bank select (four banks).
SDRAM_A, // Row (A0-A11) and column (A0-A7) address.
SDRAM_DQ, // Bidirectional dtata bus.

LED,
DEBUG
);

input FX2_CLK;
inout [7:0] FX2_FD;
input [2:0] FX2_FLAGS;

output FX2_SLRD;
output FX2_SLWR;

output FX2_PA_2;
output FX2_PA_3;
output FX2_PA_4;
output FX2_PA_5;
output FX2_PA_6;
input FX2_PA_7;

output SDRAM_CLK;
output SDRAM_CKE;
output [2:0] SDRAM_CMD;
output [1:0] SDRAM_BA;
output [1:0] SDRAM_DQM;
output [11:0] SDRAM_A;
inout [15:0] SDRAM_DQ;

output [1:0] LED;
output [23:0] DEBUG;

wire clk = FX2_CLK;

// FX2 outputs
wire FIFO2_DAV = FX2_FLAGS[0];

assign FX2_PA_3 = 1'b1;

// FX2 inputs
wire FIFO_RD, FIFO_WR, FIFO_PKTEND, FIFO_DATAIN_OE, FIFO_DATAOUT_OE;
wire [1:0] FIFO_ADR;

assign {FX2_PA_5, FX2_PA_4} = FIFO_ADR; // FPGA selects one of four FX2 FIFO's

wire FX2_SLRD = ~FIFO_RD; // FPGA reads from FX2
wire FX2_SLWR = ~FIFO_WR; // FPGA writes to FX2

assign FX2_PA_6 = ~FIFO_PKTEND; // FPGA indicates End of packet, FPGA is done writing
assign FX2_PA_2 = ~FIFO_DATAIN_OE; // FPGA wants the FX2 to drive the DATA bus

wire [7:0] FIFO_DATAIN = FX2_FD; // Data from FX2
wire [7:0] FIFO_DATAOUT; // Data to FX2

assign FX2_FD = FIFO_DATAOUT_OE ? FIFO_DATAOUT : 8'hZZ;

reg mem_done;

////////////////////////////////////////////////////////////////////////////////
//
// FX2 interface state machine
//
reg [2:0] FX2_state;

initial begin
FX2_state = 3'b000;
end


////////////////////////////////////////////////////////////////////////////////
//
// Count the number of bytes received from the host.
//
wire read_byte = (FX2_state==3'b001) & FIFO2_DAV;
reg [2:0] RxD_addr_reg;
always @(posedge clk) if(read_byte) RxD_addr_reg<=RxD_addr_reg+3'h1; else RxD_addr_reg<=3'h0;

////////////////////////////////////////////////////////////////////////////////
//
//
reg [7:0] DataIn [7:0];
always @(posedge clk) if(read_byte) DataIn[RxD_addr_reg] <= FIFO_DATAIN;

////////////////////////////////////////////////////////////////////////////////
//
//
wire [23:0] mem_addr = {DataIn[2],DataIn[1],DataIn[0]};
wire [15:0] mem_datain0 = {DataIn[5],DataIn[4]};
wire [15:0] mem_datain1 = {DataIn[7],DataIn[6]};

wire [15:0] mem_dataout;

wire mem_do = &RxD_addr_reg;
wire mem_rdwr = DataIn[3][0];
wire mem_cmd = DataIn[3][1];
wire mem_mode = DataIn[3][2];

always @(posedge clk)
case(FX2_state)
3'b000: if ( FIFO2_DAV) FX2_state <= 3'b001; // wait for data packet in FIFO2
3'b001: if (~FIFO2_DAV) FX2_state <= 3'b010; // wait until end of data packet
3'b010: if (mem_done)
begin
if (mem_rdwr) FX2_state <= 3'b100; // This is a host read.
else FX2_state <= 3'b000; // This is a host write, we're done
end
3'b100: FX2_state <= 3'b101; // turnaround cycle, switch to FIFO4
3'b101: FX2_state <= 3'b110; // write data to FIFO4
3'b110: FX2_state <= 3'b111; // write data to FIFO4
3'b111: FX2_state <= 3'b000;
default: FX2_state <= 3'b000;
endcase

assign FIFO_ADR = {FX2_state[2], 1'b0}; // FIFO2 or FIFO4
assign FIFO_RD = (FX2_state==3'b001);

// now write the count back
// assign FIFO_DATAOUT = cnt;
assign FIFO_WR = (FX2_state==3'b101) | (FX2_state==3'b110);
assign FIFO_PKTEND = (FX2_state==3'b111);
assign FIFO_DATAIN_OE = ~FX2_state[2];
assign FIFO_DATAOUT_OE = FIFO_WR;


assign FIFO_DATAOUT = FX2_state[0] ? mem_dataout[7:0] : mem_dataout[15:8];

////////////////////////////////////////////////////////////////////////////////
assign SDRAM_CLK = ~clk;
assign SDRAM_CKE = 1'b1;

wire [2:0] SDRAM_CMD_LOADMODE = 3'b000;
wire [2:0] SDRAM_CMD_REFRESH = 3'b001;
wire [2:0] SDRAM_CMD_PRECHARGE = 3'b010;
wire [2:0] SDRAM_CMD_ACTIVE = 3'b011;
wire [2:0] SDRAM_CMD_WRITE = 3'b100;
wire [2:0] SDRAM_CMD_READ = 3'b101;
//wire [2:0] SDRAM_CMD_TERMINATE = 3'b110;
wire [2:0] SDRAM_CMD_NOP = 3'b111;

reg [2:0] SDRAM_CMD;
// assign {SDRAM_RASn, SDRAM_CASn, SDRAM_WEn} = SDRAM_CMD;

reg [1:0] SDRAM_BA;

reg SDRAM_DQMR;
assign SDRAM_DQM[0] = SDRAM_DQMR;
assign SDRAM_DQM[1] = SDRAM_DQMR;

reg [11:0] SDRAM_A;
reg SDRAM_DQ_oe;
reg [15:0] SDRAM_DQ_out;
reg [15:0] SDRAM_DQ_in;

reg [4:0] SDRAM_state;

initial begin
SDRAM_state = 5'b00000;
end


wire SDRAM_state0 = (SDRAM_state == 5'b00000);
wire SDRAM_state_write0 = (SDRAM_state == 5'b00010);
wire SDRAM_state_write1 = (SDRAM_state == 5'b00011);
wire SDRAM_state_write = (SDRAM_state == 5'b00010) | (SDRAM_state == 5'b00011);
wire SDRAM_state_read = (SDRAM_state == 5'b01001);
wire SDRAM_state_read_done = (SDRAM_state == 5'b01100);

reg [7:0] refresh_counter;
reg refresh_now;
always @(posedge clk) refresh_counter<=refresh_counter+8'h1;
always @(posedge clk) refresh_now <= (refresh_now ? ~SDRAM_state0 : &refresh_counter);

reg mem_do_now;
always @(posedge clk) mem_do_now <= (mem_do_now ? ~(SDRAM_state0 & ~refresh_now) : mem_do);

always @(posedge clk)
case(SDRAM_state)
5'b00000: // 0x00
begin
if(refresh_now)
begin
SDRAM_CMD <= SDRAM_CMD_REFRESH;
SDRAM_state <= 5'b01101; // goto 0x0D
end
else
if(mem_do_now & mem_cmd & ~mem_mode)
begin
SDRAM_CMD <= SDRAM_CMD_PRECHARGE; // A18 high for all banks precharge
SDRAM_state <= 5'b01100; // goto 0x0C
end
else
if(mem_do_now & mem_cmd & mem_mode)
begin
SDRAM_CMD <= SDRAM_CMD_LOADMODE; // A[18:8]
SDRAM_state <= 5'b01100; // goto 0x0C
end
else
if(mem_do_now & ~mem_cmd)
begin
SDRAM_CMD <= SDRAM_CMD_ACTIVE;
SDRAM_state <= (mem_rdwr ? 5'b01000 : 5'b00001);
end
else
begin
SDRAM_CMD <= SDRAM_CMD_NOP;
SDRAM_state <= 5'b00000;
end
end

// write
5'b00001: // 0x01
begin
SDRAM_CMD <= SDRAM_CMD_NOP;
SDRAM_state <= 5'b00010;
end
5'b00010:
begin
SDRAM_CMD <= SDRAM_CMD_WRITE;
SDRAM_state <= 5'b00011;
end
5'b00011:
begin
SDRAM_CMD <= SDRAM_CMD_NOP;
SDRAM_state <= 5'b00100;
end
5'b00100:
begin
SDRAM_CMD <= SDRAM_CMD_NOP;
SDRAM_state <= 5'b00101;
end
5'b00101:
begin
SDRAM_CMD <= SDRAM_CMD_NOP;
SDRAM_state <= 5'b00000;
end

// read // 0x08
5'b01000:
begin
SDRAM_CMD <= SDRAM_CMD_NOP;
SDRAM_state <= 5'b01001;
end
5'b01001:
begin
SDRAM_CMD <= SDRAM_CMD_READ;
SDRAM_state <= 5'b01010;
end
5'b1010:
begin
SDRAM_CMD <= SDRAM_CMD_NOP;
SDRAM_state <= 5'b01011;
end
5'b01011:
begin
SDRAM_CMD <= SDRAM_CMD_NOP;
SDRAM_state <= 5'b01100;
end
5'b1100: // 0x0C
begin
SDRAM_CMD <= SDRAM_CMD_NOP;
SDRAM_state <= 5'b00000;
end

// auto-refresh
5'b01101: // 0x0D
begin
SDRAM_CMD <= SDRAM_CMD_NOP;
SDRAM_state <= 5'b01110;
end
5'b01110:
begin
SDRAM_CMD <= SDRAM_CMD_NOP; // 0x0E
SDRAM_state <= 5'b01111;
end
5'b01111:
begin
SDRAM_CMD <= SDRAM_CMD_NOP; // 0x00
SDRAM_state <= 5'b00000;
end

default:
begin
SDRAM_CMD <= SDRAM_CMD_NOP;
SDRAM_state <= 5'b00000;
end
endcase

//reg mem_done;
always @(posedge clk)
begin
if (SDRAM_state0) SDRAM_BA <= mem_addr[21:20];
SDRAM_A <= (SDRAM_state0 ? mem_addr[19:8] : {4'b0100, mem_addr[7:0]}); // precharge
SDRAM_DQMR <= ((SDRAM_state_read | SDRAM_state_write) ? 0 : 1);
SDRAM_DQ_oe <= SDRAM_state_write;
if (SDRAM_state_write0) SDRAM_DQ_out <= mem_datain1;
else SDRAM_DQ_out <= mem_datain0;
mem_done <= SDRAM_state_write1 | SDRAM_state_read_done;
if(SDRAM_state_read_done) SDRAM_DQ_in <= SDRAM_DQ;
end

assign SDRAM_DQ = (SDRAM_DQ_oe ? SDRAM_DQ_out : 16'hZZZZ);
assign mem_dataout = SDRAM_DQ_in;

////////////////////////////////////////////////////////////////////////////////
//
// Debug Port Assignments
//
// assign DEBUG = 24'b0;
assign DEBUG[15:0] = SDRAM_DQ_out;
assign DEBUG[21:16] = SDRAM_state;
assign DEBUG[22] = clk;
assign DEBUG[23] = SDRAM_state_write0;

assign LED[0] = &DataIn[3] | mem_addr[23] & mem_addr[22];
assign LED[1] = FX2_FLAGS[1] & FX2_FLAGS[2] & FX2_PA_7;

endmodule
[/size]
reedb
 
Posts: 14
Joined: Sun Apr 05, 2009 4:06 pm
Location: Bowen Island, Canada

It's the SDRAM example code that came with the Xylo-LM

Postby reedb » Sat Jul 04, 2009 8:03 pm

Do you have the Xylo-LM? If not I can recommend it. Comes with quite a few samples to get you started quickly. If you do have it, I can mail you the cmplete working source files. -Reed
reedb
 
Posts: 14
Joined: Sun Apr 05, 2009 4:06 pm
Location: Bowen Island, Canada

Spartan 3A starter kit VGA support

Postby reedb » Mon Jul 06, 2009 12:49 am

The Spartan 3A starter kit VGA support is nothing special. It's just three, 4 bit ladder networks (12-bits => 4096 colors!) and a VGA connector. You could duplicate this in less than an hour of hand soldering on the Xylo-LM.

These starter kits are built by a third party (Digilent in this case) and I've been told that they are sold under the cost of the BOM as a marketing device. To bad they don't spend more time/money on their sample code.
reedb
 
Posts: 14
Joined: Sun Apr 05, 2009 4:06 pm
Location: Bowen Island, Canada

Timing block memory writes, with 8 WORD SDRAM bursts

Postby reedb » Sat Jul 11, 2009 9:09 pm

Xylo LM SDRAM Test Host Program. Send and receive a block using EzUSB Driver.
Send block size in bytes: 4096, 0x00001000
Writing 4096 bytes to USB device...OK
Sent 4096 bytes in: 0.064475 seconds
63528.824219 bytes per second.
Reading 4096 bytes from USB device...OK

No doubt you can do much better with full page bursts, but this is good enough for my application. These measurements do scale (I checked) so to fill all 2MB: ~33 seconds
reedb
 
Posts: 14
Joined: Sun Apr 05, 2009 4:06 pm
Location: Bowen Island, Canada


Return to FX2 FPGA boards