URL
https://opencores.org/ocsvn/wbddr3/wbddr3/trunk
Subversion Repositories wbddr3
Compare Revisions
- This comparison shows the changes necessary to convert path
/
- from Rev 3 to Rev 4
- ↔ Reverse comparison
Rev 3 → Rev 4
/wbddr3/trunk/bench/cpp/ddrsdramsim.h
0,0 → 1,81
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: ddrsdramsim.h |
// |
// Project: A wishbone controlled DDR3 SDRAM memory controller. |
// |
// Purpose: |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, but WITHOUT |
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
#ifndef DDRSDRAMSIM_H |
#define DDRSDRAMSIM_H |
|
#define DDR_MRSET 0 |
#define DDR_REFRESH 1 |
#define DDR_PRECHARGE 2 |
#define DDR_ACTIVATE 3 |
#define DDR_WRITE 4 |
#define DDR_READ 5 |
#define DDR_ZQS 6 |
#define DDR_NOOP 7 |
|
#define NBANKS 8 |
#define NTIMESLOTS 16 |
|
class BANKINFO { |
public: |
int m_state; |
unsigned m_row; |
void tick(int cmd, unsigned addr=0); |
}; |
|
class BUSTIMESLOT { |
public: |
int m_used, m_read, m_data; |
unsigned m_addr; |
}; |
|
class DDRSDRAMSIM { |
int m_reset_state, m_reset_counts, m_memlen, m_busloc; |
unsigned *m_mem; |
BANKINFO m_bank[8]; |
BUSTIMESLOT *m_bus; |
int cmd(int,int,int,int); |
public: |
DDRSDRAMSIM(int lglen); |
unsigned operator()(int, int, |
int, int, int, int, |
int, int, int, int, |
int, int, int); |
unsigned &operator[](unsigned addr) { return m_mem[addr]; }; |
}; |
|
#endif |
/wbddr3/trunk/bench/cpp/ddrsdram_tb.cpp
0,0 → 1,444
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: ddrsdram_tb.cpp |
// |
// Project: A wishbone controlled DDR3 SDRAM memory controller. |
// |
// Purpose: To determine whether or not the wbddrsdram Verilog module works. |
// Run this program with no arguments. If the last line output |
// is "SUCCESS", you will know it works. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, but WITHOUT |
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
#include <stdio.h> |
|
#include "verilated.h" |
#include "Vwbddrsdram.h" |
#include "ddrsdramsim.h" |
|
const int BOMBCOUNT = 2048, |
SDRAMMASK = 0x3ffffff, |
LGMEMSIZE = 28; |
|
class DDRSDRAM_TB { |
long m_tickcount; |
Vwbddrsdram *m_core; |
DDRSDRAMSIM *m_sdram; |
bool m_bomb; |
public: |
|
DDRSDRAM_TB(void) { |
m_core = new Vwbddrsdram; |
m_sdram= new DDRSDRAMSIM(LGMEMSIZE); |
} |
|
unsigned &operator[](const int index) { return (*m_sdram)[index]; } |
void set(unsigned addr, unsigned v) { |
(*m_sdram)[addr] = v; |
} |
|
void tick(void) { |
m_core->i_clk = 1; |
|
m_core->i_ddr_data = (*m_sdram)( |
m_core->o_ddr_reset_n, |
m_core->o_ddr_cke, |
m_core->o_ddr_cs_n, |
m_core->o_ddr_ras_n, |
m_core->o_ddr_cas_n, |
m_core->o_ddr_we_n, |
m_core->o_ddr_dqs, |
m_core->o_ddr_dm, |
m_core->o_ddr_odt, |
m_core->o_ddr_bus_oe, |
m_core->o_ddr_addr, |
m_core->o_ddr_ba, |
m_core->o_ddr_data); |
|
printf("%08lx-WB: %s/%s %s%s%s %s@0x%08x[%08x/%08x] -- ", |
m_tickcount, |
(m_core->i_wb_cyc)?"CYC":" ", |
(m_core->i_wb_stb)?"STB":" ", |
(m_core->o_wb_stall)?"STALL":" ", |
(m_core->o_wb_ack)?"ACK":" ", |
(m_core->o_cmd_accepted)?"BUS":" ", |
(m_core->i_wb_we)?"W":"R", |
(m_core->i_wb_addr), |
(m_core->i_wb_data), |
(m_core->o_wb_data)); |
|
printf("%s%s %d%d%d%d %s%s%s%s B[%d]@%04x %08x %08x", |
(m_core->o_ddr_reset_n)?" ":"R", |
(m_core->o_ddr_cke)?"CK":" ", |
(m_core->o_ddr_cs_n), |
(m_core->o_ddr_ras_n), |
(m_core->o_ddr_cas_n), |
(m_core->o_ddr_we_n), |
// |
(m_core->o_ddr_dqs)?"D":" ", |
(m_core->o_ddr_dm)?"M":" ", |
(m_core->o_ddr_odt)?"O":" ", |
(m_core->o_ddr_bus_oe)?"E":" ", |
// |
(m_core->o_ddr_ba), |
(m_core->o_ddr_addr), |
(m_core->i_ddr_data), |
(m_core->o_ddr_data)); |
|
// Reset logic |
printf(" RST(%06x%s[%d] - %08x->%08x)", |
m_core->v__DOT__reset_timer, |
(m_core->v__DOT__reset_ztimer)?"Z":" ", |
(m_core->v__DOT__reset_address), |
(m_core->v__DOT__reset_instruction), |
(m_core->v__DOT__reset_cmd)); |
|
extern int gbl_state, gbl_counts; |
printf(" %d:%08x ", gbl_state, gbl_counts); |
|
if (m_core->v__DOT__reset_override) |
printf(" OVERRIDE"); |
printf("\n"); |
|
m_core->eval(); |
m_core->i_clk = 0; |
m_core->eval(); |
|
m_tickcount++; |
|
/* |
if ((m_core->o_wb_ack)&&(!m_core->i_wb_cyc)) { |
printf("SETTING ERR TO TRUE!!!!! ACK w/ no CYC\n"); |
// m_bomb = true; |
} |
*/ |
} |
|
void reset(void) { |
m_core->i_reset = 1; |
m_core->i_wb_cyc = 0; |
m_core->i_wb_stb = 0; |
tick(); |
m_core->i_reset = 0; |
} |
|
void wb_tick(void) { |
m_core->i_wb_cyc = 0; |
m_core->i_wb_stb = 0; |
tick(); |
} |
|
unsigned wb_read(unsigned a) { |
int errcount = 0; |
unsigned result; |
|
printf("WB-READ(%08x)\n", a); |
|
m_core->i_wb_cyc = 1; |
m_core->i_wb_stb = 1; |
m_core->i_wb_we = 0; |
m_core->i_wb_addr= a & SDRAMMASK; |
|
if (m_core->o_wb_stall) { |
while((errcount++ < BOMBCOUNT)&&(m_core->o_wb_stall)) |
tick(); |
} else |
tick(); |
|
m_core->i_wb_stb = 0; |
|
while((errcount++ < BOMBCOUNT)&&(!m_core->o_wb_ack)) |
tick(); |
|
|
result = m_core->o_wb_data; |
|
// Release the bus? |
m_core->i_wb_cyc = 0; |
m_core->i_wb_stb = 0; |
|
if(errcount >= BOMBCOUNT) { |
printf("SETTING ERR TO TRUE!!!!!\n"); |
m_bomb = true; |
} else if (!m_core->o_wb_ack) { |
printf("SETTING ERR TO TRUE--NO ACK, NO TIMEOUT\n"); |
m_bomb = true; |
} |
tick(); |
|
return result; |
} |
|
void wb_read(unsigned a, int len, unsigned *buf) { |
int errcount = 0; |
int THISBOMBCOUNT = BOMBCOUNT * len; |
int cnt, rdidx, inc; |
|
printf("WB-READ(%08x, %d)\n", a, len); |
|
while((errcount++ < BOMBCOUNT)&&(m_core->o_wb_stall)) |
wb_tick(); |
|
if (errcount >= BOMBCOUNT) { |
m_bomb = true; |
return; |
} |
|
errcount = 0; |
|
m_core->i_wb_cyc = 1; |
m_core->i_wb_stb = 1; |
m_core->i_wb_we = 0; |
m_core->i_wb_addr= a & SDRAMMASK; |
|
rdidx =0; cnt = 0; |
inc = 1; |
|
do { |
int s; |
s = (m_core->o_wb_stall==0)?0:1; |
tick(); |
if (!s) |
m_core->i_wb_addr += inc; |
cnt += (s==0)?1:0; |
if (m_core->o_wb_ack) |
buf[rdidx++] = m_core->o_wb_data; |
} while((cnt < len)&&(errcount++ < THISBOMBCOUNT)); |
|
m_core->i_wb_stb = 0; |
|
while((rdidx < len)&&(errcount++ < THISBOMBCOUNT)) { |
tick(); |
if (m_core->o_wb_ack) |
buf[rdidx++] = m_core->o_wb_data; |
} |
|
// Release the bus? |
m_core->i_wb_cyc = 0; |
|
if(errcount >= THISBOMBCOUNT) { |
printf("SETTING ERR TO TRUE!!!!! (errcount=%08x, THISBOMBCOUNT=%08x)\n", errcount, THISBOMBCOUNT); |
m_bomb = true; |
} else if (!m_core->o_wb_ack) { |
printf("SETTING ERR TO TRUE--NO ACK, NO TIMEOUT\n"); |
m_bomb = true; |
} |
tick(); |
} |
|
void wb_write(unsigned a, unsigned int v) { |
int errcount = 0; |
|
printf("WB-WRITE(%08x) = %08x\n", a, v); |
m_core->i_wb_cyc = 1; |
m_core->i_wb_stb = 1; |
m_core->i_wb_we = 1; |
m_core->i_wb_addr= a & SDRAMMASK; |
m_core->i_wb_data= v; |
|
if (m_core->o_wb_stall) |
while((errcount++ < BOMBCOUNT)&&(m_core->o_wb_stall)) |
tick(); |
tick(); |
|
m_core->i_wb_stb = 0; |
|
while((errcount++ < BOMBCOUNT)&&(!m_core->o_wb_ack)) |
tick(); |
|
// Release the bus? |
m_core->i_wb_cyc = 0; |
m_core->i_wb_stb = 0; |
|
if(errcount >= BOMBCOUNT) { |
printf("SETTING ERR TO TRUE!!!!!\n"); |
m_bomb = true; |
} tick(); |
} |
|
void wb_write(unsigned a, unsigned int ln, unsigned int *buf) { |
unsigned errcount = 0, nacks = 0; |
|
m_core->i_wb_cyc = 1; |
m_core->i_wb_stb = 1; |
for(unsigned stbcnt=0; stbcnt<ln; stbcnt++) { |
m_core->i_wb_we = 1; |
m_core->i_wb_addr= (a+stbcnt) & SDRAMMASK; |
m_core->i_wb_data= buf[stbcnt]; |
errcount = 0; |
|
while((errcount++ < BOMBCOUNT)&&(m_core->o_wb_stall)) { |
tick(); if (m_core->o_wb_ack) nacks++; |
} |
// Tick, now that we're not stalled. This is the tick |
// that gets accepted. |
tick(); if (m_core->o_wb_ack) nacks++; |
} |
|
m_core->i_wb_stb = 0; |
|
errcount = 0; |
while((nacks < ln)&&(errcount++ < BOMBCOUNT)) { |
tick(); |
if (m_core->o_wb_ack) { |
nacks++; |
errcount = 0; |
} |
} |
|
// Release the bus |
m_core->i_wb_cyc = 0; |
m_core->i_wb_stb = 0; |
|
if(errcount >= BOMBCOUNT) { |
printf("SETTING ERR TO TRUE!!!!!\n"); |
m_bomb = true; |
} tick(); |
} |
|
bool bombed(void) const { return m_bomb; } |
|
}; |
|
void uload(unsigned len, unsigned *buf) { |
FILE *fp = fopen("/dev/urandom", "r"); |
|
if ((NULL == fp)||(len != fread(buf, sizeof(unsigned), len, fp))) { |
for(int i=0; i<(int)len; i++) |
buf[i] = rand(); |
} if (NULL == fp) |
fclose(fp); |
} |
|
int main(int argc, char **argv) { |
Verilated::commandArgs(argc, argv); |
DDRSDRAM_TB *tb = new DDRSDRAM_TB; |
unsigned *rdbuf, *mbuf; |
int nw = 3, nr = 13; |
unsigned mlen = (1<<(LGMEMSIZE-2)); |
|
printf("Giving the core 140k cycles to start up\n"); |
// Before testing, let's give the unit time enough to warm up |
tb->reset(); |
for(int i=0; i<140850; i++) |
tb->wb_tick(); |
|
printf("Getting some memory ...\n"); |
rdbuf = new unsigned[mlen]; |
mbuf = new unsigned[mlen]; // Match buffer |
printf("Charging my memory with random values\n"); |
uload(mlen, rdbuf); |
|
// First test: singular reads through the memory, followed by |
// singular writes |
printf("Starting the single-read test\n"); |
for(int i=0; i<(int)mlen; i++) { |
tb->wb_write(i, rdbuf[i]); |
tb->wb_tick(); |
if ((*tb)[i] != rdbuf[i]) { |
printf("WRITE[%06x] = %08x (Expecting %08x) FAILED\n", |
i, (*tb)[i], rdbuf[i]); |
goto test_failure; |
} if (tb->bombed()) |
goto test_failure; |
|
} for(int i=0; i<(int)mlen; i++) { |
unsigned v; |
if (rdbuf[i] != (v=tb->wb_read(i))) { |
printf("READ[%06x] = %08x (Expecting %08x)\n", |
i, v, rdbuf[i]); |
goto test_failure; |
} if (tb->bombed()) |
goto test_failure; |
tb->wb_tick(); |
} |
|
// Second test: Vector writes going through all memory, followed a |
// massive vector read |
uload(mlen, rdbuf); // Get some new values |
tb->wb_write(0, mlen, rdbuf); |
if (tb->bombed()) |
goto test_failure; |
for(int i=0; i<(int)mlen; i++) { |
unsigned v; |
if (rdbuf[i] != (v=(*tb)[i])) { |
printf("V-WRITE[%06x] = %08x (Expecting %08x)\n", |
i, v, rdbuf[i]); |
goto test_failure; |
} |
} |
|
tb->wb_read( 0, mlen, mbuf); |
if (tb->bombed()) |
goto test_failure; |
for(int i=0; i<(int)mlen; i++) { |
if (rdbuf[i] != mbuf[i]) { |
printf("V-READ[%06x] = %08x (Expecting %08x)\n", |
i, mbuf[i], rdbuf[i]); |
goto test_failure; |
} |
} |
|
// Third test: Vector writes going through all memory, an prime number |
// of values at a time, followed by reads via a different prime number |
for(int i=0; i<(int)mlen; i+=nw) { |
int ln = ((int)mlen-i>nw)?nw:mlen-i; |
tb->wb_write(i, nw, &rdbuf[i]); |
for(int j=0; j<ln; j++) { |
if ((*tb)[i+j] != rdbuf[i+j]) { |
printf("P-WRITE[%06x] = %08x (Expecting %08x) FAILED\n", |
i, (*tb)[i], rdbuf[i]); |
goto test_failure; |
} |
} if (tb->bombed()) |
goto test_failure; |
} for(int i=0; i<(int)mlen; i+=nr) { |
int ln = ((int)mlen-i>nr)?nr:mlen-i; |
tb->wb_write(i, nr, &mbuf[i]); |
for(int j=0; j<ln; j++) { |
if (mbuf[i+j] != rdbuf[i+j]) { |
printf("P-READ[%06x] = %08x (Expecting %08x) FAILED\n", |
i, mbuf[i], rdbuf[i]); |
goto test_failure; |
} |
} if (tb->bombed()) |
goto test_failure; |
} |
|
|
printf("SUCCESS!!\n"); |
exit(0); |
test_failure: |
printf("FAIL-HERE\n"); |
for(int i=0; i<64; i++) |
tb->tick(); |
printf("TEST FAILED\n"); |
exit(-1); |
} |
/wbddr3/trunk/bench/cpp/ddrsdramsim.cpp
0,0 → 1,307
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: ddrsdramsim.cpp |
// |
// Project: A wishbone controlled DDR3 SDRAM memory controller. |
// |
// Purpose: |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, but WITHOUT |
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
#include <stdio.h> |
#include <assert.h> |
|
const unsigned ckCL = 5, |
ckRC = 3; |
|
#include "ddrsdramsim.h" |
void BANKINFO::tick(int cmd, unsigned addr) { |
switch(cmd) { |
case DDR_PRECHARGE: |
m_state <<= 1; |
// m_state |= 1; |
m_state &= 6; |
break; |
case DDR_ACTIVATE: |
m_state <<= 1; |
m_state |= 1; |
m_state &= 7; |
m_row = addr & 0x7fff; |
break; |
case DDR_READ: case DDR_WRITE: |
assert((m_state&7) == 7); |
break; |
case DDR_ZQS: |
assert((m_state&7) == 0); |
break; |
case DDR_NOOP: |
m_state <<= 1; |
m_state |= (m_state&2)>>1; |
break; |
default: |
break; |
} |
} |
|
int gbl_state, gbl_counts; |
|
DDRSDRAMSIM::DDRSDRAMSIM(int lglen) { |
m_memlen = (1<<(lglen-2)); |
m_mem = new unsigned[m_memlen]; |
m_reset_state = 0; |
m_reset_counts= 0; |
m_bus = new BUSTIMESLOT[NTIMESLOTS]; |
for(int i=0; i<NTIMESLOTS; i++) |
m_bus[i].m_used = 0; |
m_busloc = 0; |
} |
|
unsigned DDRSDRAMSIM::operator()(int reset_n, int cke, |
int csn, int rasn, int casn, int wen, |
int dqs, int dm, int odt, int busoe, |
int addr, int ba, int data) { |
|
int cmd = (reset_n?0:32)|(cke?0:16)|(csn?8:0) |
|(rasn?4:0)|(casn?2:0)|(wen?1:0); |
if ((m_reset_state!=0)&&(reset_n==0)) { |
m_reset_state = 0; |
m_reset_counts = 0; |
} else if (m_reset_state < 16) { |
switch(m_reset_state) { |
case 0: |
m_reset_counts++; |
if (reset_n) { |
assert(m_reset_counts > 40000); |
m_reset_counts = 0; |
m_reset_state = 1; |
} break; |
case 1: |
m_reset_counts++; |
if (cke) { |
assert(m_reset_counts > 100000); |
m_reset_counts = 0; |
m_reset_state = 2; |
} break; |
case 2: |
m_reset_counts++; |
if (cmd != DDR_NOOP) { |
assert(m_reset_counts > 147); |
m_reset_counts = 0; |
m_reset_state = 3; |
assert(cmd == DDR_MRSET); |
assert(ba == 2); |
assert(addr == 0x040); |
} break; |
case 3: |
m_reset_counts++; |
if (cmd != DDR_NOOP) { |
assert(m_reset_counts > 3); |
m_reset_counts = 0; |
m_reset_state = 4; |
assert(cmd == DDR_MRSET); |
assert(ba == 1); |
assert(addr == 0x847); |
} break; |
case 4: |
m_reset_counts++; |
if (cmd != DDR_NOOP) { |
assert(m_reset_counts > 3); |
m_reset_counts = 0; |
m_reset_state = 5; |
assert(cmd == DDR_MRSET); |
assert(ba == 0); |
assert(addr == 0x210); |
} break; |
case 5: |
m_reset_counts++; |
if (cmd != DDR_NOOP) { |
assert(m_reset_counts > 12); |
m_reset_counts = 0; |
m_reset_state = 6; |
assert(cmd == DDR_ZQS); |
assert(addr == 0x40); |
} break; |
case 6: |
m_reset_counts++; |
if (cmd != DDR_NOOP) { |
assert(m_reset_counts > 512); |
m_reset_counts = 0; |
m_reset_state = 7; |
assert(cmd == DDR_PRECHARGE); |
assert(addr == 0x40); |
} break; |
case 7: |
m_reset_counts++; |
if (cmd != DDR_NOOP) { |
assert(m_reset_counts > 3); |
m_reset_counts = 0; |
m_reset_state = 8; |
assert(cmd == DDR_REFRESH); |
} break; |
case 8: |
m_reset_counts++; |
assert(cmd == DDR_NOOP); |
if (m_reset_counts > 140) |
m_reset_state = 16; |
break; |
default: |
break; |
} |
|
gbl_state = m_reset_state; |
gbl_counts= m_reset_counts; |
} else if (cke == 0) { |
assert(0&&"Clock not enabled!"); |
} else switch(cmd) { |
case DDR_MRSET: |
assert(0&&"Modes should only be set in reset startup"); |
for(int i=0; i<NBANKS; i++) |
m_bank[i].tick(DDR_MRSET,0); |
break; |
case DDR_REFRESH: |
for(int i=0; i<NBANKS; i++) |
m_bank[i].tick(DDR_REFRESH,0); |
break; |
case DDR_PRECHARGE: |
if (addr & 0x40) { |
// Precharge all |
for(int i=0; i<NBANKS; i++) |
m_bank[i].tick(DDR_PRECHARGE,0); |
} else { |
m_bank[ba].tick(DDR_PRECHARGE,0); |
for(int i=0; i<NBANKS; i++) |
if (ba != i) |
m_bank[i].tick(DDR_NOOP,0); |
} |
break; |
case DDR_ACTIVATE: |
m_bank[ba].tick(DDR_ACTIVATE,addr); |
for(int i=0; i<NBANKS; i++) |
if (i!=ba) m_bank[i].tick(DDR_NOOP,0); |
break; |
case DDR_WRITE: |
{ |
for(int i=0; i<NBANKS; i++) |
m_bank[i].tick(DDR_WRITE,addr); |
unsigned addr = m_bank[ba].m_row; |
addr <<= 13; |
addr |= ba; |
addr <<= 10; |
addr |= addr; |
addr &= ~3; |
|
BUSTIMESLOT *tp; |
|
tp = &m_bus[(m_busloc+ckCL+0)&(NTIMESLOTS-1)]; |
tp->m_addr = addr ; |
tp->m_used = 1; |
tp->m_read = 0; |
|
tp = &m_bus[(m_busloc+ckCL+1)&(NTIMESLOTS-1)]; |
tp->m_addr = addr+1; |
tp->m_used = 1; |
tp->m_read = 0; |
|
tp = &m_bus[(m_busloc+ckCL+2)&(NTIMESLOTS-1)]; |
tp->m_addr = addr+2; |
tp->m_used = 1; |
tp->m_read = 0; |
|
tp = &m_bus[(m_busloc+ckCL+3)&(NTIMESLOTS-1)]; |
tp->m_addr = addr+3; |
tp->m_used = 1; |
tp->m_read = 0; |
} break; |
case DDR_READ: |
{ |
for(int i=0; i<NBANKS; i++) |
m_bank[i].tick(DDR_READ,addr); |
unsigned addr = m_bank[ba].m_row; |
addr <<= 13; |
addr |= ba; |
addr <<= 10; |
addr |= addr; |
addr &= ~3; |
|
BUSTIMESLOT *tp; |
|
tp = &m_bus[(m_busloc+ckCL+0)&(NTIMESLOTS-1)]; |
tp->m_data = m_mem[addr]; |
tp->m_addr = addr; |
tp->m_used = 1; |
tp->m_read = 1; |
|
tp = &m_bus[(m_busloc+ckCL+1)&(NTIMESLOTS-1)]; |
tp->m_data = m_mem[addr+1]; |
tp->m_addr = addr+1; |
tp->m_used = 1; |
tp->m_read = 1; |
|
tp = &m_bus[(m_busloc+ckCL+2)&(NTIMESLOTS-1)]; |
tp->m_data = m_mem[addr+2]; |
tp->m_addr = addr+2; |
tp->m_used = 1; |
tp->m_read = 1; |
|
tp = &m_bus[(m_busloc+ckCL+3)&(NTIMESLOTS-1)]; |
tp->m_data = m_mem[addr+3]; |
tp->m_addr = addr+3; |
tp->m_used = 1; |
tp->m_read = 1; |
} break; |
case DDR_ZQS: |
assert(0&&"Sim does not support ZQS outside of startup"); |
break; |
case DDR_NOOP: |
for(int i=0; i<NBANKS; i++) |
m_bank[i].tick(DDR_NOOP,addr); |
break; |
default: // We are deselecteda |
for(int i=0; i<NBANKS; i++) |
m_bank[i].tick(DDR_NOOP,addr); |
break; |
} |
|
m_busloc = (m_busloc+1)&(NTIMESLOTS-1); |
|
BUSTIMESLOT *ts = &m_bus[m_busloc]; |
unsigned vl = ts->m_data; |
assert( ((!ts->m_used)||(busoe)) |
|| ((ts->m_used)&&(ts->m_read))); |
|
assert((!ts->m_used)||(ts->m_addr < (unsigned)m_memlen)); |
if ((ts->m_used)&&(!ts->m_read)&&(!dm)) |
m_mem[ts->m_addr] = data; |
ts->m_used = 0; |
ts->m_read = 0; |
ts->m_addr = -1; |
return (!busoe)?vl:data; |
} |
|
/wbddr3/trunk/bench/cpp/Makefile
0,0 → 1,71
################################################################################ |
## |
## Filename: Makefile |
## |
## Project: A wishbone controlled DDR3 SDRAM memory controller. |
## |
## Purpose: This coordinates the build of the singular test bench C++ |
## program found in this directory: ddrsdram_tb. |
## |
## Creator: Dan Gisselquist, Ph.D. |
## Gisselquist Technology, LLC |
## |
################################################################################ |
## |
## Copyright (C) 2015-2016, Gisselquist Technology, LLC |
## |
## This program is free software (firmware): you can redistribute it and/or |
## modify it under the terms of the GNU General Public License as published |
## by the Free Software Foundation, either version 3 of the License, or (at |
## your option) any later version. |
## |
## This program is distributed in the hope that it will be useful, but WITHOUT |
## ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
## for more details. |
## |
## You should have received a copy of the GNU General Public License along |
## with this program. (It's in the $(ROOT)/doc directory, run make with no |
## target there if the PDF file isn't present.) If not, see |
## <http://www.gnu.org/licenses/> for a copy. |
## |
## License: GPL, v3, as defined and found on www.gnu.org, |
## http://www.gnu.org/licenses/gpl.html |
## |
## |
################################################################################ |
## |
## |
all: $(OBJDIR)/ ddrsdram_tb |
|
CXX := g++ |
OBJDIR := obj-pc |
YYMMDD := `date +%Y%m%d` |
RTLD := ../../rtl |
VOBJDR := $(RTLD)/obj_dir |
VROOT := /usr/share/verilator |
VINC := -I$(VROOT)/include -I$(VOBJDR) |
CFLAGS := -Wall -c -Og -g -I. $(VINC) |
SOURCES := ddrsdramsim.cpp ddrsdram_tb.cpp |
VOBJDR := $(RTLD)/obj_dir |
VLIB := $(VROOT)/include/verilated.cpp |
|
$(OBJDIR)/: |
@bash -c "if [ ! -e $(OBJDIR) ]; then mkdir -p $(OBJDIR); fi" |
|
$(OBJDIR)/ddrsdramsim.o: ddrsdramsim.cpp ddrsdramsim.h $(VOBJDR)/Vwbddrsdram.h |
$(CXX) $(CFLAGS) ddrsdramsim.cpp -o $@ |
$(OBJDIR)/verilated.o: $(VLIB) $(OBJDIR)/ |
$(CXX) $(CFLAGS) $(VLIB) -o $@ |
$(OBJDIR)/ddrsdram_tb.o: ddrsdram_tb.cpp ddrsdramsim.h $(VOBJDR)/Vwbddrsdram.h |
$(CXX) $(CFLAGS) ddrsdram_tb.cpp -o $@ |
|
OBJECTS := ddrsdramsim.o ddrsdram_tb.o verilated.o |
OBJECTSDR:= $(addprefix $(OBJDIR)/,$(OBJECTS)) |
ddrsdram_tb: $(OBJECTSDR) $(VOBJDR)/Vwbddrsdram__ALL.a |
$(CXX) -Wall $(INCS) $^ -o $@ |
|
.PHONY: clean |
clean: |
rm -rf $(OBJDIR)/ ddrsdram_tb |
|
/wbddr3/trunk/rtl/wbddrsdram.v
44,6 → 44,7
`define DDR_ACTIVATE 4'b0011 |
`define DDR_WRITE 4'b0100 |
`define DDR_READ 4'b0101 |
`define DDR_ZQS 4'b0110 |
`define DDR_NOOP 4'b0111 |
//`define DDR_DESELECT 4'b1??? |
// |
68,10 → 69,12
o_wb_ack, o_wb_stall, o_wb_data, |
o_ddr_reset_n, o_ddr_cke, |
o_ddr_cs_n, o_ddr_ras_n, o_ddr_cas_n, o_ddr_we_n, |
o_ddr_dqs, o_ddr_dm, o_ddr_odt, o_ddr_bus_dir, |
o_ddr_addr, o_ddr_ba, o_ddr_data, i_ddr_data); |
o_ddr_dqs, o_ddr_dm, o_ddr_odt, o_ddr_bus_oe, |
o_ddr_addr, o_ddr_ba, o_ddr_data, i_ddr_data, |
o_cmd_accepted); |
parameter CKREFI4 = 13'd6240, // 4 * 7.8us at 200 MHz clock |
CKRFC = 140; |
CKRFC = 140, |
CKXPR = CKRFC+5+2; // Clocks per tXPR timeout |
input i_clk, i_reset; |
// Wishbone inputs |
input i_wb_cyc, i_wb_stb, i_wb_we; |
87,7 → 90,7
output reg o_ddr_cs_n, o_ddr_ras_n, o_ddr_cas_n,o_ddr_we_n; |
// DQS outputs:set to 3'b010 when data is active, 3'b100 (i.e. 2'bzz) ow |
output wire o_ddr_dqs; |
output reg o_ddr_dm, o_ddr_odt, o_ddr_bus_dir; |
output reg o_ddr_dm, o_ddr_odt, o_ddr_bus_oe; |
// Address outputs |
output reg [13:0] o_ddr_addr; |
output reg [2:0] o_ddr_ba; |
94,7 → 97,12
// And the data inputs and outputs |
output reg [31:0] o_ddr_data; |
input i_ddr_data; |
// And just for the test bench |
output reg o_cmd_accepted; |
|
always @(posedge i_clk) |
o_cmd_accepted <= (i_wb_stb)&&(~o_wb_stall); |
|
reg drive_dqs; |
|
// The pending transaction |
174,13 → 182,8
; |
else if (reset_instruction[`DDR_RSTDONE]) |
reset_override <= 1'b0; |
else if (reset_instruction[`DDR_RSTTIMER]) |
begin |
if (reset_instruction[`DDR_NOPTIMER]) |
reset_cmd <= { `DDR_NOOP, reset_instruction[18:0]}; |
end else begin |
else |
reset_cmd <= reset_instruction[22:0]; |
end |
always @(posedge i_clk) |
if (i_reset) |
o_ddr_cke <= 1'b0; |
187,13 → 190,13
else if ((reset_override)&&(reset_ztimer)) |
o_ddr_cke <= reset_instruction[`DDR_CKEBIT]; |
|
initial reset_ztimer = 1'b1; // Is the timer zero? |
initial reset_timer = 17'h00; |
initial reset_ztimer = 1'b0; // Is the timer zero? |
initial reset_timer = 17'h01; |
always @(posedge i_clk) |
if (i_reset) |
begin |
reset_ztimer <= 1'b0; |
reset_timer <= 17'h00; |
reset_timer <= 17'd1; |
end else if (!reset_ztimer) |
begin |
reset_ztimer <= (reset_timer == 17'h01); |
204,22 → 207,74
reset_timer <= reset_instruction[16:0]; |
end |
|
|
wire [18:0] w_ckXPR = CKXPR, w_ckRST = 4, w_ckRP = 3, |
w_ckRFC = CKRFC; |
always @(posedge i_clk) |
case(reset_address) // RSTDONE, TIMER, CKE, ?? |
if (i_reset) |
reset_instruction <= { 4'h4, `DDR_NOOP, 19'd40_000 }; |
else case(reset_address) // RSTDONE, TIMER, CKE, ?? |
// 1. Reset asserted (active low) for 200 us. (@200MHz) |
4'h0: reset_instruction <= { 4'h4, `DDR_NOOP, 19'd40_000 }; |
// 2. Reset de-asserted, wait 500 us before asserting CKE |
4'h1: reset_instruction <= { 4'h6, `DDR_NOOP, 19'd100_000 }; |
4'h2: reset_instruction <= { 4'h7, `DDR_NOOP, 19'd40_000 }; |
4'h3: reset_instruction <= { 4'h3, `DDR_MRSET, 3'h0, 3'h0, 1'b0, 3'h1, 1'b0, 1'b0, 3'h1, 1'b0, 1'b0, 2'b00 }; // MRS |
4'h4: reset_instruction <= { 4'h7, `DDR_NOOP, 19'd40_000 }; |
4'h5: reset_instruction <= { 4'h3, `DDR_MRSET, 3'h2, 5'h0, 2'b00, 1'b0, 1'b0, 1'b1, 3'b0, 3'b0 }; // MRS2 |
4'h6: reset_instruction <= { 4'h7, `DDR_NOOP, 19'd40_000 }; |
4'h7: reset_instruction <= { 4'h3, `DDR_MRSET, 3'h1, 3'h0, 1'b0, 1'b1, 1'b0, 1'b0, 1'b0, 2'b0, 1'b1, 1'b0, 2'b0, 1'b1, 1'b1, 1'b0 }; // MRS1 |
// 3. Assert CKE, wait minimum of Reset CKE Exit time |
4'h2: reset_instruction <= { 4'h7, `DDR_NOOP, w_ckXPR }; |
// 4. Look MR2. (1CK, no TIMER) |
4'h3: reset_instruction <= { 4'h3, `DDR_MRSET, 3'h2, |
5'h0, 2'b00, 1'b0, 1'b0, 1'b1, 3'b0, 3'b0 }; // MRS2 |
// 3. Wait 4 clocks (tMRD) |
4'h4: reset_instruction <= { 4'h7, `DDR_NOOP, 19'h04 }; |
// 5. Set MR1 |
4'h5: reset_instruction <= { 4'h3, `DDR_MRSET, 3'h1, |
3'h0, // Reserved for Future Use (RFU) |
1'b0, // Qoff - output buffer enabled |
1'b1, // TDQS ... enabled |
1'b0, // RFU |
1'b0, // High order bit, Rtt_Nom (3'b011) |
1'b0, // RFU |
// |
1'b0, // Disable write-leveling |
1'b1, // Mid order bit of Rtt_Nom |
1'b0, // High order bit of Output Drvr Impedence Ctrl |
2'b0, // Additive latency = 0 |
1'b1, // Low order bit of Rtt_Nom |
1'b1, // DIC set to 2'b01 |
1'b1 }; // MRS1, DLL enable |
// 7. Wait another 4 clocks |
4'h6: reset_instruction <= { 4'h7, `DDR_NOOP, 19'h04 }; |
// 8. Send MRS0 |
4'h7: reset_instruction <= { 4'h3, `DDR_MRSET, 3'h0, |
3'b0, // Reserved for future use |
1'b0, // PPD control, (slow exit(DLL off)) |
3'b1, // Write recovery for auto precharge |
1'b0, // DLL Reset (No) |
// |
1'b0, // TM mode normal |
3'b01, // High 3-bits, CAS latency (=4'b0010 = 4'd5) |
1'b0, // Read burst type = nibble sequential |
1'b0, // Low bit of cas latency |
2'b0 }; // Burst length = 8 (Fixed) |
// 9. Wait tMOD, is max(12 clocks, 15ns) |
4'h8: reset_instruction <= { 4'h7, `DDR_NOOP, 19'h0c }; |
// 10. Issue a ZQCL command to start ZQ calibration, A10 is high |
4'h9: reset_instruction <= { 4'h7, `DDR_ZQS, 8'h0, 1'b1, 10'h0}; |
//11.Wait for both tDLLK and tZQinit completed, both are 512 cks |
4'ha: reset_instruction <= { 4'h7, `DDR_NOOP, 19'd512 }; |
// 12. Precharge all command |
4'hb: reset_instruction <= { 4'h7, `DDR_PRECHARGE, 8'h0, 1'b1, 10'h0 }; |
// 13. Wait for the precharge to complete |
4'hc: reset_instruction <= { 4'h7, `DDR_NOOP, w_ckRP }; |
// 14. A single Auto Refresh commands |
4'hd: reset_instruction <= { 4'h7, `DDR_REFRESH, 19'h00 }; |
// 15. Wait for the auto refresh to complete |
4'he: reset_instruction <= { 4'h7, `DDR_NOOP, w_ckRFC }; |
// Two Auto Refresh commands |
default: |
reset_instruction <={4'hb, `DDR_NOOP, 19'd00_000 }; |
endcase |
// reset_instruction <= reset_mem[reset_address]; |
|
initial reset_address = 4'h0; |
always @(posedge i_clk) |
if (i_reset) |
reset_address <= 4'h0; |
388,7 → 443,8
bus_subaddr[2] <= bus_subaddr[1]; |
bus_subaddr[1] <= bus_subaddr[0]; |
bus_subaddr[0] <= 2'h3; |
if (cmd[22:19] == `DDR_READ) |
if ((!reset_override)&&(!need_refresh)&&(!need_close_bank) |
&&(!need_open_bank)&&(valid_bank)) |
begin |
bus_active[3:0]<= 4'hf; // Once per clock |
bus_read[3:0] <= 4'hf; // These will be reads |
395,13 → 451,8
bus_subaddr[3] <= 2'h0; |
bus_subaddr[2] <= 2'h1; |
bus_subaddr[1] <= 2'h2; |
end else if (cmd == `DDR_WRITE) |
begin |
bus_active[3:0] <= 4'hf; |
// bus_read[7:4] = 4'h0; |
bus_subaddr[3] <= 2'h0; |
bus_subaddr[2] <= 2'h1; |
bus_subaddr[1] <= 2'h2; |
|
bus_read[3:0] <= (r_we)? 4'h0:4'hf; |
end |
end |
|
432,7 → 483,7
r_data <= i_wb_data; |
r_row <= i_wb_addr[25:11]; |
r_bank <= i_wb_addr[10:8]; |
r_col <= { i_wb_addr[7:2], 2'b00 }; // 9:2 |
r_col <= { i_wb_addr[7:0], 2'b00 }; // 9:2 |
r_sub <= i_wb_addr[1:0]; |
|
// pre-emptive work |
464,7 → 515,7
|
close_bank_cmd <= (maybe_close_next_bank) |
? { `DDR_PRECHARGE, r_nxt_bank, r_nxt_row[14:10], 1'b0, r_nxt_row[9:0] } |
: { `DDR_PRECHARGE, r_bank, r_row[15:11], 1'b0, r_row[9:0] }; |
: { `DDR_PRECHARGE, r_bank, r_row[14:10], 1'b0, r_row[9:0] }; |
|
|
need_open_bank <= (r_pending)&&(bank_active[r_bank][1:0]==2'b00) |
523,9 → 574,15
// Okay, let's look at the last assignment in our chain. It should look |
// something like: |
always @(posedge i_clk) |
o_ddr_reset_n <= (~reset_override)||(reset_instruction[`DDR_RSTBIT]); |
if (i_reset) |
o_ddr_reset_n <= 1'b0; |
else if (reset_ztimer) |
o_ddr_reset_n <= reset_instruction[`DDR_RSTBIT]; |
always @(posedge i_clk) |
o_ddr_cke <= (~reset_override)||(reset_instruction[`DDR_CKEBIT]); |
if (i_reset) |
o_ddr_cke <= 1'b0; |
else if (reset_ztimer) |
o_ddr_cke <= reset_instruction[`DDR_CKEBIT]; |
always @(posedge i_clk) |
begin |
r_move <= 1'b0; |
543,7 → 600,7
cmd <= rw_cmd; |
r_move <= 1'b1; |
end else |
cmd <= { `DDR_NOOP, rw_cmd[20:0] }; |
cmd <= { `DDR_NOOP, rw_cmd[(`DDR_WEBIT-1):0] }; |
end |
|
reg [31:0] bus_data[8:0]; |
561,8 → 618,11
|
// Need to set o_wb_dqs high one clock prior to any read. |
// As per spec, ODT = 0 during reads |
assign o_ddr_bus_dir = bus_read[8]; |
assign o_ddr_odt = o_ddr_bus_dir; |
assign o_ddr_bus_oe = ~bus_read[8]; |
|
// ODT must be in high impedence while reset_n=0, then it can be set |
// to low or high. |
assign o_ddr_odt = o_ddr_bus_oe; |
|
|
endmodule |