URL
https://opencores.org/ocsvn/wbddr3/wbddr3/trunk
Subversion Repositories wbddr3
Compare Revisions
- This comparison shows the changes necessary to convert path
/wbddr3/trunk
- from Rev 15 to Rev 16
- ↔ Reverse comparison
Rev 15 → Rev 16
/bench/cpp/ddrsdramsim.h
49,6 → 49,7
|
#define NBANKS 8 |
#define NTIMESLOTS 32 |
#define NWIDTH 2 |
|
class BANKINFO { |
public: |
75,11 → 76,19
public: |
BANKINFO m_bank[8]; |
DDRSDRAMSIM(int lglen); |
unsigned operator()(int, int, |
unsigned apply(int, int, |
int, int, int, int, |
int, int, int, int, |
int, int, int); |
unsigned &operator[](unsigned addr) { return m_mem[addr]; }; |
unsigned 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) { |
return apply(reset_n, cke, csn, rasn, casn, wen, dqs, dm, odt, |
busoe, addr, ba, data); |
} |
unsigned &mem(unsigned addr) { return m_mem[addr]; }; |
unsigned &operator[](unsigned addr) { return mem(addr); }; |
}; |
|
#endif |
/bench/cpp/pddrsim.cpp
0,0 → 1,87
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: pddrsim.cpp |
// |
// Project: A wishbone controlled DDR3 SDRAM memory controller. |
// |
// Purpose: To expand a DDR3 SDRAM controllers influence across multiple |
// clocks. Hence, if the DDR3 SDRAM controller runs at half |
// the clock rate of the DDR3-SDRAM, this will expand it to the full |
// clock rate. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 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 <stdlib.h> |
#include "ddrsdramsim.h" |
#include "pddrsim.h" |
|
unsigned long PDDRSIM::operator()(int reset_n, int cke, int busoe, |
unsigned cmda, unsigned cmdb, unsigned long data) { |
int csn, rasn, casn, wen, dqs, dm, odt, addr, ba; |
unsigned hdata, ldata; |
unsigned long odata; |
|
// if ((reset_n)&&(cke)) printf("PDDR: %08x/%08x/%016lx\n", cmda, cmdb, data); |
csn = (cmda >> 26)&1; |
rasn = (cmda >> 25)&1; |
casn = (cmda >> 24)&1; |
wen = (cmda >> 23)&1; |
ba = (cmda >> 20)&0x7; // 3 bits |
addr = (cmda >> 6)&0x3fff; // 14 bits |
dqs = (cmda >> 5)&0x01; // 1 bits |
dm = (cmda >> 1)&0x0f; // 4 bits |
odt = (cmda )&0x01; // 1 bits |
|
// if ((reset_n)&&(cke)) printf("PDDR: %s/%02x/%s\n", (dqs)?"DQS":" ",dm,(odt)?"ODT":" "); |
|
hdata = DDRSDRAMSIM::apply(reset_n, cke, csn, rasn, casn, wen, |
dqs, dm, odt, busoe, addr, ba, (unsigned)(data>>32)); |
|
csn = (cmdb >> 26)&1; |
rasn = (cmdb >> 25)&1; |
casn = (cmdb >> 24)&1; |
wen = (cmdb >> 23)&1; |
ba = (cmdb >> 20)&0x7; // 3 bits |
addr = (cmdb >> 6)&0x3fff; // 14 bits |
dqs = (cmdb >> 5)&0x01; // 1 bits |
dm = (cmdb >> 1)&0x0f; // 4 bits |
odt = (cmdb )&0x01; // 1 bits |
|
// if ((reset_n)&&(cke)) printf("PDDR: %s/%02x/%s\n", (dqs)?"DQS":" ",dm,(odt)?"ODT":" "); |
|
ldata = DDRSDRAMSIM::apply(reset_n, cke, csn, rasn, casn, wen, |
dqs, dm, odt, busoe, addr, ba, (unsigned)(data&(~(-1l<<32)))); |
|
|
odata = (((unsigned long)hdata)<<32)|((unsigned long)ldata); |
return odata; |
} |
|
/bench/cpp/ddrsdram_tb.cpp
41,7 → 41,8
|
#include "verilated.h" |
#include "Vwbddrsdram.h" |
#include "ddrsdramsim.h" |
#include "pddrsim.h" |
// #include "ddrsdramsim.h" |
|
const int BOMBCOUNT = 2048, |
SDRAMMASK = 0x3ffffff, |
50,18 → 51,18
class DDRSDRAM_TB { |
long m_tickcount; |
Vwbddrsdram *m_core; |
DDRSDRAMSIM *m_sdram; |
PDDRSIM *m_sdram; |
bool m_bomb; |
public: |
|
DDRSDRAM_TB(void) { |
m_core = new Vwbddrsdram; |
m_sdram= new DDRSDRAMSIM(LGMEMSIZE); |
m_sdram= new PDDRSIM(LGMEMSIZE); |
} |
|
unsigned &operator[](const int index) { return (*m_sdram)[index]; } |
void set(unsigned addr, unsigned v) { |
(*m_sdram)[addr] = v; |
unsigned long operator[](const int index) { return (*m_sdram)[index]; } |
void set(unsigned addr, unsigned long v) { |
m_sdram->set(addr, v); |
} |
|
void tick(void) { |
70,32 → 71,36
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_cmd_a, |
m_core->o_ddr_cmd_b, |
m_core->o_ddr_data); |
|
bool writeout = (!m_core->v__DOT__reset_override); |
|
int cmd; |
cmd = (m_core->o_ddr_reset_n?0:32) |
int cmda, cmdb, cmd; |
cmda = (m_core->o_ddr_reset_n?0:32) |
|(m_core->o_ddr_cke?0:16) |
|(m_core->o_ddr_cs_n?8:0) |
|(m_core->o_ddr_ras_n?4:0) |
|(m_core->o_ddr_cas_n?2:0) |
|(m_core->o_ddr_we_n?1:0); |
if ((cmd&0x0f)==DDR_REFRESH) |
|((m_core->o_ddr_cmd_a&(1<<26))?8:0) |
|((m_core->o_ddr_cmd_a&(1<<25))?4:0) |
|((m_core->o_ddr_cmd_a&(1<<24))?2:0) |
|((m_core->o_ddr_cmd_a&(1<<23))?1:0); |
cmdb = (m_core->o_ddr_reset_n?0:32) |
|(m_core->o_ddr_cke?0:16) |
|((m_core->o_ddr_cmd_b&(1<<26))?8:0) |
|((m_core->o_ddr_cmd_b&(1<<25))?4:0) |
|((m_core->o_ddr_cmd_b&(1<<24))?2:0) |
|((m_core->o_ddr_cmd_b&(1<<23))?1:0); |
cmd = m_core->o_ddr_cmd_b; |
if ((cmdb&0x0f)!=DDR_NOOP) |
cmd = m_core->o_ddr_cmd_a; |
if ((cmda&0x0f)==DDR_REFRESH) |
writeout = true; |
if ((cmdb&0x0f)==DDR_REFRESH) |
writeout = true; |
|
if (writeout) { |
printf("%08lx-WB: %s/%s %s%s%s %s@0x%08x[%08x/%08x] -- ", |
printf("%08lx-WB: %s/%s %s%s%s %s@0x%08x[%016lx/%016lx] -- ", |
m_tickcount, |
(m_core->i_wb_cyc)?"CYC":" ", |
(m_core->i_wb_stb)?"STB":" ", |
107,25 → 112,28
(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", |
printf("%s%s %d%d%d%d,%d%d%d%d %s%s[%x%x]%s%s B[%d,%d]@%04x %016lx %016lx", |
(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), |
(cmda&0x08)?1:0, (cmda&0x04)?1:0, |
(cmda&0x02)?1:0, (cmda&0x01)?1:0, |
(cmdb&0x08)?1:0, (cmdb&0x04)?1:0, |
(cmdb&0x02)?1:0, (cmdb&0x01)?1:0, |
// |
(m_core->o_ddr_dqs)?"D":" ", |
(m_core->o_ddr_dm)?"M":" ", |
(m_core->o_ddr_odt)?"O":" ", |
((m_core->o_ddr_cmd_a>>5)&1)?"D":"-", |
((m_core->o_ddr_cmd_b>>5)&1)?"D":"-", |
((m_core->o_ddr_cmd_a>>1)&0x0f), |
((m_core->o_ddr_cmd_b>>1)&0x0f), |
(m_core->o_ddr_cmd_a&1)?"O":" ", |
(m_core->o_ddr_bus_oe)?"E":" ", |
// |
(m_core->o_ddr_ba), |
(m_core->o_ddr_addr), |
(m_core->o_ddr_cmd_a>>(6+14))&0x07, // Get the bank address |
(m_core->o_ddr_cmd_b>>(6+14))&0x07, // Get the bank address |
((cmd>>6)&0x03fff), // The command address |
(m_core->i_ddr_data), |
(m_core->o_ddr_data)); |
|
printf(" FIFO[%x,%x](%s,%d,%08x)", |
printf(" FIFO[%x,%x](%s,%d,%016lx)", |
m_core->v__DOT__bus_fifo_head, |
m_core->v__DOT__bus_fifo_tail, |
(m_core->v__DOT__bus_fifo_new[m_core->v__DOT__bus_fifo_tail])?"N":"o", |
132,16 → 140,17
m_core->v__DOT__bus_fifo_sub[m_core->v__DOT__bus_fifo_tail], |
m_core->v__DOT__bus_fifo_data[m_core->v__DOT__bus_fifo_tail]); |
|
printf(" BUS[A%03x/R%03x/N%03x/K%03x/%d]", |
printf(" BUS[A%03x/R%03x/N%03x/K%03x/T%03x/%02x]", |
(m_core->v__DOT__bus_active), |
(m_core->v__DOT__bus_read), |
(m_core->v__DOT__bus_new), |
(m_core->v__DOT__bus_ack), |
(m_core->v__DOT__bus_subaddr[8])); |
(m_core->v__DOT__bus_odt), |
(m_core->v__DOT__bus_subaddr)); |
|
/* |
// Reset logic |
printf(" RST(%06x%s[%d] - %08x->%08x)", |
printf(" RST(%06x%s[%2d] - %08x->%08x)", |
m_core->v__DOT__reset_timer, |
(m_core->v__DOT__reset_ztimer)?"Z":" ", |
(m_core->v__DOT__reset_address), |
149,7 → 158,7
(m_core->v__DOT__reset_cmd)); |
*/ |
|
printf(" R_%s%03x[%d]%04x:%d/%08x", |
printf(" R_%s%03x[%d]%04x:%d/%02x/%016lx", |
(!m_core->v__DOT__r_pending)?"_" |
:(m_core->v__DOT__r_we)?"W":"R", |
(m_core->v__DOT__r_row), |
156,9 → 165,10
(m_core->v__DOT__r_bank), |
(m_core->v__DOT__r_col), |
(m_core->v__DOT__r_sub), |
(m_core->v__DOT__r_sel), |
(m_core->v__DOT__r_data)); |
|
printf(" S_%s%03x[%d]%04x:%d/%08x%s%s%s", |
printf(" S_%s%03x[%d]%04x:%d/%02x/%016lx%s%s%s", |
(!m_core->v__DOT__s_pending)?"_" |
:(m_core->v__DOT__s_we)?"W":"R", |
(m_core->v__DOT__s_row), |
165,6 → 175,7
(m_core->v__DOT__s_bank), |
(m_core->v__DOT__s_col), |
(m_core->v__DOT__s_sub), |
(m_core->v__DOT__s_sel), |
(m_core->v__DOT__s_data), |
(m_core->v__DOT__w_s_match)?"M":" ", |
(m_core->v__DOT__pipe_stall)?"P":" ", |
180,13 → 191,14
//:(m_core->v__DOT__maybe_close_next_bank)?"c":"N", |
(m_core->v__DOT__need_open_bank)?"O":"K"); |
// :(m_core->v__DOT__maybe_open_next_bank)?"o":"K"); |
|
for(int i=0; i<8; i++) { |
printf("%s%s%s%x%x@%x%s", |
printf("%s%s%s%04x|%04x@%x%s", |
(m_core->v__DOT__r_bank==i)?"R":"[", |
((m_core->v__DOT__bank_open)&(1<<i))?"+":" ", |
((m_core->v__DOT__bank_closed)&(1<<i))?"-":" ", |
m_core->v__DOT__bank_status[i], |
m_sdram->m_bank[i].m_state&0x0f, |
m_sdram->bank_state(i), |
m_core->v__DOT__bank_address[i], |
(m_core->v__DOT__r_nxt_bank==i)?"N":"]"); |
} |
203,14 → 215,13
(m_core->v__DOT__need_close_bank)?"C":" ", |
(m_core->v__DOT__need_open_bank)?"O":" ", |
(m_core->v__DOT__valid_bank)?"V":" ", |
(m_core->v__DOT__r_move)?"R":" ", |
(m_core->v__DOT__maybe_close_next_bank)?"c":" ", |
(m_core->v__DOT__maybe_open_next_bank)?"o":" ", |
(m_core->v__DOT__pre_valid)?"p":" ", |
(m_core->v__DOT__w_r_valid)?"r":" ", |
(m_core->v__DOT__w_s_valid)?"s":" "); |
(m_core->v__DOT__w_s_valid)?"s":" ", |
(m_core->v__DOT__pre_ack)?"k":" "); |
|
/* |
// Refresh logic |
printf(" F%s%05x:%x/%s", |
(m_core->v__DOT__refresh_ztimer)?"Z":" ", |
217,21 → 228,32
m_core->v__DOT__refresh_counter, |
m_core->v__DOT__refresh_addr, |
(m_core->v__DOT__need_refresh)?"N":" "); |
*/ |
|
if (m_core->v__DOT__reset_override) |
printf(" OVERRIDE"); |
//if(m_core->v__DOT__last_open_bank)printf(" LST-OPEN"); |
switch(cmd) { |
switch(cmda) { |
case DDR_MRSET: printf("%13s", " MRSET"); break; |
case DDR_REFRESH: printf("%13s", " REFRESH"); break; |
case DDR_PRECHARGE: printf("%9s%3s", " PRECHARGE", ((m_core->o_ddr_cmd_a>>6)&0x400)?"-ALL":""); break; |
case DDR_ACTIVATE: printf("%13s", " ACTIVATE"); break; |
case DDR_WRITE: printf("%13s", " WRITE"); break; |
case DDR_READ: printf("%13s", " READ"); break; |
case DDR_ZQS: printf("%13s", " ZQS"); break; |
case DDR_NOOP: printf("%13s", " NOOP"); break; |
default: printf(" UCMD(%02x)", cmda); break; |
} |
|
switch(cmdb) { |
case DDR_MRSET: printf(" MRSET"); break; |
case DDR_REFRESH: printf(" REFRESH"); break; |
case DDR_PRECHARGE: printf(" PRECHARGE%s", (m_core->o_ddr_addr&0x400)?"-ALL":""); break; |
case DDR_PRECHARGE: printf(" PRECHARGE%s", ((m_core->o_ddr_cmd_b>>6)&0x400)?"-ALL":""); break; |
case DDR_ACTIVATE: printf(" ACTIVATE"); break; |
case DDR_WRITE: printf(" WRITE"); break; |
case DDR_READ: printf(" READ"); break; |
case DDR_ZQS: printf(" ZQS"); break; |
case DDR_NOOP: printf(" NOOP"); break; |
default: printf(" Unknown-CMD(%02x)", cmd); break; |
default: printf(" UCMD(%02x)", cmdb); break; |
} |
|
// Decode the command |
277,9 → 299,9
assert(!m_core->o_wb_ack); |
} |
|
unsigned wb_read(unsigned a) { |
unsigned long wb_read(unsigned a) { |
int errcount = 0; |
unsigned result; |
unsigned long result; |
|
printf("WB-READ(%08x)\n", a); |
|
324,7 → 346,7
return result; |
} |
|
void wb_read(unsigned a, int len, unsigned *buf) { |
void wb_read(unsigned a, int len, unsigned long *buf) { |
int errcount = 0; |
int THISBOMBCOUNT = BOMBCOUNT * len; |
int cnt, rdidx, inc; |
358,7 → 380,7
cnt += (s==0)?1:0; |
if (m_core->o_wb_ack) { |
buf[rdidx] = m_core->o_wb_data; |
printf("WB-READ[%08x] = %08x\n", a+rdidx, |
printf("WB-READ[%08x] = %016lx\n", a+rdidx, |
m_core->o_wb_data); |
if (buf[rdidx] != (*this)[a+rdidx]) { |
printf("READ-FAIL\n"); |
374,7 → 396,7
tick(); |
if (m_core->o_wb_ack) { |
buf[rdidx] = m_core->o_wb_data; |
printf("WB-READ[%08x] = %08x\n", a+rdidx, |
printf("WB-READ[%08x] = %016lx\n", a+rdidx, |
m_core->o_wb_data); |
if (buf[rdidx] != (*this)[a+rdidx]) { |
printf("READ-FAIL\n"); |
399,14 → 421,15
assert(!m_core->o_wb_ack); |
} |
|
void wb_write(unsigned a, unsigned int v) { |
void wb_write(unsigned a, unsigned long v) { |
int errcount = 0; |
|
printf("WB-WRITE(%08x) = %08x\n", a, v); |
printf("WB-WRITE(%08x) = %016lx\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_sel = 0x0ff; |
m_core->i_wb_data= v; |
|
if (m_core->o_wb_stall) |
431,7 → 454,7
assert(!m_core->o_wb_ack); |
} |
|
void wb_write(unsigned a, unsigned int ln, unsigned int *buf) { |
void wb_write(unsigned a, unsigned int ln, unsigned long *buf) { |
unsigned errcount = 0, nacks = 0; |
|
m_core->i_wb_cyc = 1; |
439,6 → 462,7
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_sel = 0x0ff; |
m_core->i_wb_data= buf[stbcnt]; |
errcount = 0; |
|
477,12 → 501,13
|
}; |
|
void uload(unsigned len, unsigned *buf) { |
void uload(unsigned len, unsigned long *buf) { |
FILE *fp = fopen("/dev/urandom", "r"); |
|
if ((NULL == fp)||(len != fread(buf, sizeof(unsigned), len, fp))) { |
if ((NULL == fp)||(len != fread(buf, sizeof(unsigned long), len, fp))) { |
for(int i=0; i<(int)len; i++) |
buf[i] = rand(); |
buf[i] = (((unsigned long)rand())<<32) |
|(((unsigned long)rand())&(-1l<<32)); |
} if (NULL == fp) |
fclose(fp); |
} |
490,8 → 515,8
int main(int argc, char **argv) { |
Verilated::commandArgs(argc, argv); |
DDRSDRAM_TB *tb = new DDRSDRAM_TB; |
unsigned *rdbuf, *mbuf; |
unsigned mlen = (1<<(LGMEMSIZE-2)); |
unsigned long *rdbuf, *mbuf; |
unsigned mlen = (1<<(LGMEMSIZE-3)); |
|
printf("Giving the core 140k cycles to start up\n"); |
// Before testing, let's give the unit time enough to warm up |
505,8 → 530,8
mlen = 1<<16; |
|
printf("Getting some memory ...\n"); |
rdbuf = new unsigned[mlen]; |
mbuf = new unsigned[mlen]; // Match buffer |
rdbuf = new unsigned long[mlen]; |
mbuf = new unsigned long[mlen]; // Match buffer |
printf("Charging my memory with random values\n"); |
uload(mlen, rdbuf); |
|
522,24 → 547,25
|
#ifdef CASE_TESTS |
{ |
unsigned v; |
unsigned long v; |
for(int i=0; i<8; i++) { |
tb->wb_write(i, rdbuf[i]); |
if ((v=tb->wb_read(i)) != rdbuf[i]) { |
printf("CASE-1, %08x -> MEM[%08x] -> %08x FAILED (R/W not equal)\n", rdbuf[i], i, v); |
printf("CASE-1, %16lx -> MEM[%08x] -> %16lx FAILED (R/W not equal)\n", rdbuf[i], i, v); |
goto test_failure; |
} |
} else printf("MATCH[%d] = %16lx\n", i, rdbuf[i]); |
} |
} |
|
// Now repeat, hitting a different bank with each command |
{ |
unsigned v, a; |
unsigned a; |
unsigned long v; |
for(int i=0; i<8; i++) { |
a = 1087 + i*1031; |
tb->wb_write(a, rdbuf[a]); |
if ((v=tb->wb_read(a)) != rdbuf[a]) { |
printf("CASE-2, %08x -> MEM[%08x] -> %08x FAILED (R/W not equal)\n", rdbuf[a], a, v); |
printf("CASE-2, %016lx -> MEM[%08x] -> %016lx FAILED (R/W not equal)\n", rdbuf[a], a, v); |
goto test_failure; |
} |
} |
547,12 → 573,13
|
// And again, hitting the same bank with each command |
{ |
unsigned v, a; |
unsigned a; |
unsigned long v; |
for(int i=0; i<8; i++) { |
a = 1109 + i*1024; |
tb->wb_write(a, rdbuf[a]); |
if ((v=tb->wb_read(a)) != rdbuf[a]) { |
printf("CASE-3, %08x -> MEM[%08x] -> %08x FAILED (R/W not equal)\n", rdbuf[a], a, v); |
printf("CASE-3, %016lx -> MEM[%08x] -> %016lx FAILED (R/W not equal)\n", rdbuf[a], a, v); |
goto test_failure; |
} |
} |
560,7 → 587,8
|
// Same thing, but writing all first before reading |
{ |
unsigned v, a; |
unsigned a; |
unsigned long v; |
for(int i=0; i<8; i++) { |
a = 1109 + i*1024; |
tb->wb_write(a, rdbuf[a]); |
567,7 → 595,7
} for(int i=0; i<8; i++) { |
a = 1109 + i*1024; |
if ((v=tb->wb_read(a)) != rdbuf[a]) { |
printf("CASE-4, %08x -> MEM[%08x] -> %08x FAILED (R/W not equal)\n", rdbuf[a], a, v); |
printf("CASE-4, %016lx -> MEM[%08x] -> %016lx FAILED (R/W not equal)\n", rdbuf[a], a, v); |
goto test_failure; |
} |
} |
575,12 → 603,13
|
// And a quick pipeline test |
{ |
unsigned v, mbuf[8], a = 379; |
unsigned long v, mbuf[8]; |
unsigned a = 379; |
tb->wb_write(0, 8, &rdbuf[379]); |
for(int i=0; i<8; i++) { |
a = 379+i; |
if ((v=(*tb)[i]) != rdbuf[a]) { |
printf("CASE-5, %08x -> MEM[%08x] -> %08x FAILED (R/W not equal)\n", rdbuf[a], i, v); |
printf("CASE-5, %016lx -> MEM[%08x] -> %016lx FAILED (R/W not equal)\n", rdbuf[a], i, v); |
goto test_failure; |
} |
} tb->wb_read(0, 8, mbuf); |
587,7 → 616,7
for(int i=0; i<8; i++) { |
a = 379+i; |
if (mbuf[i] != rdbuf[a]) { |
printf("CASE-6, %08x -> MEM[%08x] -> %08x FAILED (R/W not equal)\n", rdbuf[a], i, mbuf[i]); |
printf("CASE-6, %016lx -> MEM[%08x] -> %016lx FAILED (R/W not equal)\n", rdbuf[a], i, mbuf[i]); |
goto test_failure; |
} |
} |
602,7 → 631,7
tb->wb_write(i, rdbuf[i]); |
tb->wb_tick(); |
if ((*tb)[i] != rdbuf[i]) { |
printf("WRITE[%06x] = %08x (Expecting %08x) FAILED\n", |
printf("WRITE[%06x] = %016lx (Expecting %016lx) FAILED\n", |
i, (*tb)[i], rdbuf[i]); |
goto test_failure; |
} if (tb->bombed()) |
614,14 → 643,14
// If we aren't doing the write test, we still need to charge |
// the memory for the read test. Here we do it manually. |
for(int i=0; i<(int)mlen; i++) |
(*tb)[i] = rdbuf[i]; |
tb->set(i, rdbuf[i]); |
#endif // !SINGULAR_WRITE && SINGULAR_READ |
#endif // SINGULAR_WRITE |
#ifdef SINGULAR_READ |
for(int i=0; i<(int)mlen; i++) { |
unsigned v; |
unsigned long v; |
if (rdbuf[i] != (v=tb->wb_read(i))) { |
printf("READ[%06x] = %08x (Expecting %08x)\n", |
printf("READ[%06x] = %016lx (Expecting %016lx)\n", |
i, v, rdbuf[i]); |
goto test_failure; |
} if (tb->bombed()) |
639,9 → 668,9
if (tb->bombed()) |
goto test_failure; |
for(int i=0; i<(int)mlen; i++) { |
unsigned v; |
unsigned long v; |
if (rdbuf[i] != (v=(*tb)[i])) { |
printf("V-WRITE[%06x] = %08x (Expecting %08x)\n", |
printf("V-WRITE[%06x] = %016lx (Expecting %016lx)\n", |
i, v, rdbuf[i]); |
goto test_failure; |
} |
662,7 → 691,7
goto test_failure; |
for(int i=0; i<(int)mlen; i++) { |
if (rdbuf[i] != mbuf[i]) { |
printf("V-READ[%06x] = %08x (Expecting %08x)\n", |
printf("V-READ[%06x] = %016lx (Expecting %016lx)\n", |
i, mbuf[i], rdbuf[i]); |
goto test_failure; |
} |
681,7 → 710,7
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", |
printf("P-WRITE[%06x] = %016lx (Expecting %016lx) FAILED\n", |
i, (*tb)[i], rdbuf[i]); |
goto test_failure; |
} |
695,10 → 724,11
// If we aren't doing the write test, we still need to charge |
// the memory for the read test. Here we do it manually. |
for(int i=0; i<(int)mlen; i++) |
(*tb)[i] = rdbuf[i]; |
tb->set(i, rdbuf[i]); |
#endif |
#endif |
#ifdef PRIMEVEC_READ |
printf("Starting the prime-vector read test\n"); |
{ |
int nr = 13; |
for(int i=0; i<(int)mlen; i+=nr) { |
706,7 → 736,7
tb->wb_read(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", |
printf("P-READ[%06x] = %016lx (Expecting %016lx) FAILED\n", |
i, mbuf[i], rdbuf[i]); |
goto test_failure; |
} |
725,7 → 755,7
int loc = (i*3889)&(mlen-1); |
tb->wb_write(loc, rdbuf[loc]); |
if ((*tb)[loc] != rdbuf[loc]) { |
printf("R-WRITE[%06x] = %08x (Expecting %08x) FAILED\n", |
printf("R-WRITE[%06x] = %016lx (Expecting %016lx) FAILED\n", |
i, (*tb)[loc], rdbuf[loc]); |
goto test_failure; |
} if (tb->bombed()) |
735,7 → 765,7
#ifdef SKIP_READ |
uload(mlen, rdbuf); // Get some new values |
for(int i=0; i<(int)mlen; i++) |
(*tb)[i] = rdbuf[i]; |
tb->set(i, rdbuf[i]); |
#endif // !SKIP_WRITE && SKIP_READ |
#endif |
#ifdef SKIP_READ |
743,7 → 773,7
int loc = (i*7477)&(mlen-1); |
mbuf[loc] = tb->wb_read(loc); |
if (mbuf[loc] != rdbuf[loc]) { |
printf("R-READ[%06x] = %08x (Expecting %08x) FAILED\n", |
printf("R-READ[%06x] = %016lx (Expecting %016lx) FAILED\n", |
loc, mbuf[loc], rdbuf[loc]); |
goto test_failure; |
} if (tb->bombed()) |
/bench/cpp/pddrsim.h
0,0 → 1,71
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: pddrsim.h |
// |
// Project: A wishbone controlled DDR3 SDRAM memory controller. |
// |
// Purpose: To expand a DDR3 SDRAM controllers influence across multiple |
// clocks. Hence, if the DDR3 SDRAM controller runs at half |
// the clock rate of the DDR3-SDRAM, this will expand it to the full |
// clock rate. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 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 PDDRSIM_H |
#define PDDRSIM_H |
|
#include "ddrsdramsim.h" |
|
class PDDRSIM : protected DDRSDRAMSIM { |
public: |
PDDRSIM(int lglen) : DDRSDRAMSIM(lglen) {}; |
unsigned long operator()(int, int, int, |
unsigned, unsigned, unsigned long); |
unsigned long operator[](unsigned addr) { |
unsigned hdata, ldata; |
unsigned long odata; |
hdata = mem(addr<<1); |
ldata = mem((addr<<1)+1); |
|
odata = (((unsigned long)hdata)<<32)|((unsigned long)ldata); |
return odata; }; |
void set(unsigned addr, unsigned long data) { |
unsigned hdata, ldata; |
hdata = (unsigned long)(data>>32); |
ldata = (unsigned long)(data); |
|
mem((addr<<1) ) = hdata; |
mem((addr<<1)+1) = ldata; |
}; |
unsigned bank_state(int bankid) { |
return (m_bank[bankid].m_state & 0x0ffff); }; |
}; |
|
#endif |
/bench/cpp/ddrsdramsim.cpp
38,21 → 38,29
#include <stdio.h> |
#include <assert.h> |
|
#include "ddrsdramsim.h" |
|
#define PREFIX "DDR3-SDRAM" |
const unsigned ckCL = 5, |
ckRC = 10, |
ckRAS = 7, |
ckRFC = 320, // Clocks from refresh to activate |
ckREFI = 1560; // 7.8us @ 200MHz = 7.8e-6 * 200e6 = 1560 |
/* |
const unsigned nREF = 4, |
ckREFIn = nREF*ckREFI - (nREF-1) * ckRFC; |
*/ |
const unsigned nREF = 1, |
ckREFIn = ckREFI; |
const unsigned ckCL = 6, |
ckCWL = 5, |
ckRP = 6, |
ckWR = 6, |
ckRAS = 15, |
ckRC = 20, |
ckMRD = 4, |
ckMOD = 12, |
ckZQinit = 512, |
ckODT = ckCWL-2, |
ckRFC = 64, // Clocks from refresh to activate |
ckREFI = 1560*NWIDTH, // 7.8us @ 200MHz = 7.8e-6 * 200e6 = 1560 |
DDR_MR2 = 0x0040 | (((ckCWL-5)&7)<<3), |
DDR_MR1 = 0x0044, |
DDR_MR0 = 0x0000 | (((ckCL-4)&0x07)<<4) | ((ckCL>11)?0x4:0) |
|((ckWR==16)?0 |
:(ckWR>=8)?((((ckWR-8)>>1)+8)<<9) |
:((ckWR-5+1)<<9)), |
nREF = 1; |
|
#include "ddrsdramsim.h" |
|
BANKINFO::BANKINFO(void) { |
m_state = 0; m_row = 0; m_wcounter = 0; m_min_time_before_precharge=0; |
} |
65,7 → 73,8
assert(m_state == 0); |
break; |
case DDR_PRECHARGE: |
m_state = 6; |
// assert((m_state&((1<<ckRP)-1)) == ((1<<ckRP)-1)); |
m_state &= -2; |
// While the specification allows precharging an already |
// precharged bank, we can keep that from happening |
// here: |
72,6 → 81,7
// assert(m_state&7); |
// Only problem is, this will currently break our |
// refresh logic. |
/* |
if (m_min_time_before_precharge != 0) { |
printf("BANK-FAIL: TIME-BEFORE-PRECHARGE = %d (should be zero)\n", m_min_time_before_precharge); |
assert(m_min_time_before_precharge == 0); |
79,12 → 89,16
printf("BANK-FAIL: TIME-BEFORE-ACTIVATE = %d (should be zero)\n", m_min_time_before_activate); |
assert(m_min_time_before_activate==0); |
} |
*/ |
break; |
case DDR_ACTIVATE: |
assert((m_state&((1<<ckRP)-1)) == 0); |
if (((m_state&7)!=0)&&((addr&0x7fff) != m_row)) { |
printf("BANK-FAIL: Attempt to Activate an already active bank without closing it first (m_state = %x)\n", m_state); |
assert((m_state&7)==0); |
} |
|
/* |
if (m_wcounter != 0) { |
printf("BANK-FAIL: ACTIVATE too soon after write (wcounter = %d)\n", m_wcounter); |
assert(m_wcounter == 0); |
92,8 → 106,10
printf("BANK-FAIL: ACTIVATE too soon after last activate, (ctr=%d)\n", m_min_time_before_activate); |
assert(m_min_time_before_activate==0); |
} |
*/ |
m_state = 1; |
m_row = addr & 0x7fff; |
printf("BANK -- Setting address to %04x\n", m_row); |
m_min_time_before_precharge = ckRAS; |
m_min_time_before_activate = ckRC; |
break; |
102,7 → 118,11
assert(m_wcounter == 0); |
else |
m_wcounter = 3+4+4; |
assert((m_state&7) == 7); |
if ((m_state&((1<<ckRP)-1)) != ((1<<ckRP)-1)) { |
printf(PREFIX "::R/W Error: m_state = %08x, ckRP = %d (%08x)\n", |
m_state, ckRP, ((1<<ckRP)-1)); |
assert((m_state&((1<<ckRP)-1)) == ((1<<ckRP)-1)); |
} |
if (m_min_time_before_precharge) |
m_min_time_before_precharge--; |
if (m_min_time_before_activate) |
109,7 → 129,7
m_min_time_before_activate--; |
break; |
case DDR_ZQS: |
assert((m_state&7) == 0); |
assert((m_state&((1<<ckRP)-1)) == 0); |
if (m_min_time_before_precharge) |
m_min_time_before_precharge--; |
if (m_min_time_before_activate) |
118,7 → 138,7
case DDR_NOOP: |
m_state <<= 1; |
m_state |= (m_state&2)>>1; |
m_state &= 0x0f; |
m_state &= ((1<<ckRP)-1); |
if (m_min_time_before_precharge) |
m_min_time_before_precharge--; |
if (m_min_time_before_activate) |
136,6 → 156,7
m_mem = new unsigned[m_memlen]; |
m_reset_state = 0; |
m_reset_counts= 0; |
assert(NTIMESLOTS > ckCL+3); |
m_bus = new BUSTIMESLOT[NTIMESLOTS]; |
for(int i=0; i<NTIMESLOTS; i++) |
m_bus[i].m_used = 0; |
144,7 → 165,7
m_busloc = 0; |
} |
|
unsigned DDRSDRAMSIM::operator()(int reset_n, int cke, |
unsigned DDRSDRAMSIM::apply(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) { |
156,11 → 177,12
m_reset_state = 0; |
m_reset_counts = 0; |
} else if (m_reset_state < 16) { |
// printf(PREFIX "::Reset-CMD = %02x,BA=%d,ADDR=%04x, counts = %d\n", cmd, ba, addr, m_reset_counts); |
switch(m_reset_state) { |
case 0: |
m_reset_counts++; |
if (reset_n) { |
assert(m_reset_counts > 40000); |
assert(m_reset_counts > 40000*NWIDTH); |
m_reset_counts = 0; |
m_reset_state = 1; |
} break; |
167,7 → 189,7
case 1: |
m_reset_counts++; |
if (cke) { |
assert(m_reset_counts > 100000); |
assert(m_reset_counts > 100000*NWIDTH); |
m_reset_counts = 0; |
m_reset_state = 2; |
} break; |
175,25 → 197,26
m_reset_counts++; |
assert(cke); |
if (cmd != DDR_NOOP) { |
assert(m_reset_counts > 147); |
assert(m_reset_counts > (int)(ckRFC+2*NWIDTH)); |
m_reset_counts = 0; |
m_reset_state = 3; |
assert(cmd == DDR_MRSET); |
// Set MR2 |
assert(ba == 2); |
assert(addr == 0x040); |
printf(PREFIX "::Checking DDR-MR2(%04x) against %04x\n", addr, DDR_MR2); |
assert(addr == DDR_MR2); |
} break; |
case 3: |
m_reset_counts++; |
assert(cke); |
if (cmd != DDR_NOOP) { |
// assert(m_reset_counts > 3); |
assert(m_reset_counts >= (int)ckMRD); |
m_reset_counts = 0; |
m_reset_state = 4; |
assert(cmd == DDR_MRSET); |
// Set MR1 |
assert(ba == 1); |
assert(addr == 0x844); |
assert(addr == DDR_MR1); |
} break; |
case 4: |
m_reset_counts++; |
200,13 → 223,13
assert(cke); |
if (cmd != DDR_NOOP) { |
printf(PREFIX "::RESET-CMD[4]: %d:%08x[%d]@0x%04x\n", cmd, m_reset_counts, ba, addr); |
assert(m_reset_counts > 3); |
assert(m_reset_counts >= (int)ckMRD); |
m_reset_counts = 0; |
m_reset_state = 5; |
assert(cmd == DDR_MRSET); |
// Set MR0 |
assert(ba == 0); |
assert(addr == 0x210); |
assert(addr == DDR_MR0); |
} break; |
case 5: |
m_reset_counts++; |
213,7 → 236,7
assert(cke); |
if (cmd != DDR_NOOP) { |
printf(PREFIX "::RESET-CMD[5]: %d:%08x[%d]@0x%04x\n", cmd, m_reset_counts, ba, addr); |
assert(m_reset_counts > 11); |
assert(m_reset_counts >= (int)ckMOD); |
m_reset_counts = 0; |
m_reset_state = 6; |
assert(cmd == DDR_ZQS); |
224,7 → 247,7
assert(cke); |
if (cmd != DDR_NOOP) { |
printf(PREFIX "::RESET-CMD[6]: %d:%08x[%d]@0x%04x\n", cmd, m_reset_counts, ba, addr); |
assert(m_reset_counts > 512); |
assert(m_reset_counts >= (int)ckZQinit); |
m_reset_counts = 0; |
m_reset_state = 7; |
assert(cmd == DDR_PRECHARGE); |
235,7 → 258,7
assert(cke); |
if (cmd != DDR_NOOP) { |
printf(PREFIX "::RESET-CMD[7]: %d:%08x[%d]@0x%04x\n", cmd, m_reset_counts, ba, addr); |
assert(m_reset_counts > 3); |
assert(m_reset_counts >= (int)ckRP); |
m_reset_counts = 0; |
m_reset_state = 8; |
assert(cmd == DDR_REFRESH); |
245,7 → 268,7
m_reset_counts++; |
assert(cke); |
assert(cmd == DDR_NOOP); |
if (m_reset_counts > 140) { |
if (m_reset_counts > (int)ckRFC) { |
m_reset_state = 16; |
printf(PREFIX ": Leaving reset state\n"); |
} |
282,7 → 305,10
// In operational mode!! |
|
m_clocks_since_refresh++; |
assert(m_clocks_since_refresh < (int)ckREFIn); |
if (m_clocks_since_refresh > (int)ckREFI) { |
printf(PREFIX "::ERROR! Clocks since refresh = %d, which isn\'t less than %d\n", m_clocks_since_refresh, ckREFI); |
} |
assert(m_clocks_since_refresh <= (int)ckREFI); |
switch(cmd) { |
case DDR_MRSET: |
assert(0&&"Modes should only be set in reset startup"); |
298,9 → 324,11
case DDR_PRECHARGE: |
if (addr & 0x400) { |
// Precharge all |
printf(PREFIX "::Precharging all banks\n"); |
for(int i=0; i<NBANKS; i++) |
m_bank[i].tick(DDR_PRECHARGE,0); |
} else { |
printf(PREFIX "::Precharging bank [%d]\n", ba); |
m_bank[ba].tick(DDR_PRECHARGE,0); |
for(int i=0; i<NBANKS; i++) |
if (ba != i) |
308,7 → 336,10
} |
break; |
case DDR_ACTIVATE: |
assert(m_clocks_since_refresh >= (int)ckRFC); |
if (m_clocks_since_refresh < (int)ckRFC) { |
printf(PREFIX "::ACTIVATE -- not enough clocks since refresh, %d < %d should be true\n", m_clocks_since_refresh, ckRFC); |
assert(m_clocks_since_refresh >= (int)ckRFC); |
} |
printf(PREFIX "::Activating bank %d, address %08x\n", ba, addr); |
m_bank[ba].tick(DDR_ACTIVATE,addr); |
for(int i=0; i<NBANKS; i++) |
330,7 → 361,7
caddr >>= 1; |
|
BUSTIMESLOT *tp; |
int offset = m_busloc+ckCL+1; |
int offset = m_busloc+ckCWL+1; |
|
tp = &m_bus[(offset+0)&(NTIMESLOTS-1)]; |
// printf("Setting bus timeslots from (now=%d)+%d=%d to now+%d+3\n", m_busloc, ckCL,(m_busloc+ckCL)&(NTIMESLOTS-1), ckCL); |
369,7 → 400,12
caddr >>= 1; |
|
BUSTIMESLOT *tp; |
|
|
printf(PREFIX"::READ(%03x:%d:%03x => %08x) Queuing %08x:%08x:%08x:%08x\n", |
m_bank[ba].m_row, ba, addr, |
caddr, |
m_mem[caddr ], m_mem[caddr+1], |
m_mem[caddr+2], m_mem[caddr+3]); |
int offset = (m_busloc+ckCL+1)&(NTIMESLOTS-1); |
tp = &m_bus[(offset)&(NTIMESLOTS-1)]; |
tp->m_data = m_mem[caddr]; |
418,7 → 454,7
break; |
} |
} if (flag) { |
printf("DQS = %d BUSLOC = %d\n", dqs, (m_busloc+1)&(NTIMESLOTS-1)); |
printf(PREFIX "::DQS = %d BUSLOC = %d\n", dqs, (m_busloc+1)&(NTIMESLOTS-1)); |
for(int i=0; i<5; i++) { |
int bl = (m_busloc+1+i)&(NTIMESLOTS-1); |
nxtts = &m_bus[bl]; |
434,9 → 470,15
} |
|
ts = &m_bus[(m_busloc+1)&(NTIMESLOTS-1)]; |
if (dqs) |
if (dqs) { |
/* |
if ((!ts->m_rtt)||(!m_last_rtt)) { |
printf(PREFIX "ODT(dqs is true) m_rtt is %s and last_rtt is %s. Both should be true if DQS. (ERROR!)\n", |
(ts->m_rtt)?"true":"false", |
(m_last_rtt)?"true":"false"); |
} */ |
assert((ts->m_rtt)&&(m_last_rtt)); |
else if (!m_last_dqs) |
} else if (!m_last_dqs) |
assert(!m_last_rtt); |
} |
|
445,6 → 487,7
ts = &m_bus[m_busloc]; |
nxtts = &m_bus[(m_busloc+1)&(NTIMESLOTS-1)]; |
unsigned vl = ts->m_data; |
|
assert( ((!ts->m_used)||(busoe)) |
|| ((ts->m_used)&&(ts->m_read)&&(!busoe)) |
|| ((ts->m_used)&&(!ts->m_read)&&(busoe)) |
462,12 → 505,19
assert(!dqs); |
|
assert((!ts->m_used)||(ts->m_addr < (unsigned)m_memlen)); |
if ((ts->m_used)&&(!ts->m_read)&&(!dm)) { |
printf(PREFIX "::Setting MEM[%08x] = %08x\n", ts->m_addr, data); |
m_mem[ts->m_addr] = data; |
if ((ts->m_used)&&(!ts->m_read)&&(dm != 0x0f)) { |
printf(PREFIX "::Setting MEM[%08x] = %08x (%02x)\n", ts->m_addr, data, dm); |
unsigned mask = 0; |
if (dm&0x08) mask = 0x0ff; |
mask <<= 8; if (dm&0x004) mask |= 0x0ff; |
mask <<= 8; if (dm&0x002) mask |= 0x0ff; |
mask <<= 8; if (dm&0x001) mask |= 0x0ff; |
m_mem[ts->m_addr] = (data & (~mask)) | (m_mem[ts->m_addr]&mask); |
} else if ((ts->m_used)&&(ts->m_read)) { |
printf(PREFIX ":: %08x\n", vl); |
} |
|
m_bus[(m_busloc+3)&(NTIMESLOTS-1)].m_rtt = (odt)&&(reset_n); |
m_bus[(m_busloc+ckODT-1)&(NTIMESLOTS-1)].m_rtt = (odt)&&(reset_n); |
ts->m_used = 0; |
ts->m_read = 0; |
ts->m_addr = -1; |
/bench/cpp/Makefile
44,7 → 44,7
VROOT := /usr/share/verilator |
VINC := -I$(VROOT)/include -I$(VOBJDR) |
CFLAGS := -Wall -c -Og -g -I. $(VINC) |
SOURCES := ddrsdramsim.cpp ddrsdram_tb.cpp |
SOURCES := pddrsim.cpp ddrsdramsim.cpp ddrsdram_tb.cpp |
VOBJDR := $(RTLD)/obj_dir |
VLIB := $(VROOT)/include/verilated.cpp |
|
54,14 → 54,17
$(OBJDIR)/: |
@bash -c "if [ ! -e $(OBJDIR) ]; then mkdir -p $(OBJDIR); fi" |
|
$(OBJDIR)/ddrsdramsim.o: ddrsdramsim.cpp ddrsdramsim.h $(VOBJDR)/Vwbddrsdram.h |
$(OBJDIR)/pddrsim.o: pddrsim.cpp pddrsim.h ddrsdramsim.h |
$(CXX) $(CFLAGS) pddrsim.cpp -o $@ |
# $(VOBJDR)/Vwbddrsdram.h |
$(OBJDIR)/ddrsdramsim.o: ddrsdramsim.cpp ddrsdramsim.h |
$(CXX) $(CFLAGS) ddrsdramsim.cpp -o $@ |
$(OBJDIR)/verilated.o: $(VLIB) $(OBJDIR)/ |
$(OBJDIR)/verilated.o: $(VLIB) |
$(CXX) $(CFLAGS) $(VLIB) -o $@ |
$(OBJDIR)/ddrsdram_tb.o: ddrsdram_tb.cpp ddrsdramsim.h $(VOBJDR)/Vwbddrsdram.h |
$(OBJDIR)/ddrsdram_tb.o: ddrsdram_tb.cpp pddrsim.h $(VOBJDR)/Vwbddrsdram.h |
$(CXX) $(CFLAGS) ddrsdram_tb.cpp -o $@ |
|
OBJECTS := ddrsdramsim.o ddrsdram_tb.o verilated.o |
OBJECTS := ddrsdramsim.o pddrsim.o ddrsdram_tb.o verilated.o |
OBJECTSDR:= $(addprefix $(OBJDIR)/,$(OBJECTS)) |
ddrsdram_tb: $(OBJECTSDR) $(VOBJDR)/Vwbddrsdram__ALL.a |
$(CXX) -Wall $(INCS) $^ -o $@ |
/rtl/wbddrsdram.v
2,9 → 2,16
// |
// Filename: wbddrsdram.v |
// |
// Project: OpenArty, an entirely open SoC based upon the Arty platform |
// Project: A wishbone controlled DDR3 SDRAM memory controller. |
// Used in: OpenArty, an entirely open SoC based upon the Arty platform |
// |
// Purpose: |
// Purpose: To control a DDR3-1333 (9-9-9) memory from a wishbone bus. |
// In our particular implementation, there will be two command |
// clocks (2.5 ns) per FPGA clock (i_clk) at 5 ns, and 64-bits transferred |
// per FPGA clock. However, since the memory is focused around 128-bit |
// word transfers, attempts to transfer other than adjacent 64-bit words |
// will (of necessity) suffer stalls. Please see the documentation for |
// more details of how this controller works. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
70,131 → 77,60
`define DDR_BABITS 3 // BABITS are really from 18:16, they are 3 bits |
`define DDR_ADDR_BITS 14 |
// |
`define BUSREG 7 |
`define BUSNOW 8 |
|
// |
module wbddrsdram(i_clk, i_reset, |
// Wishbone inputs |
i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, i_wb_data, |
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_oe, |
o_ddr_addr, o_ddr_ba, o_ddr_data, i_ddr_data); |
parameter CKRBITS = 13, // Bits in CKREFI4 |
CKREFI = 13'd1560, // 4 * 7.8us at 200 MHz clock |
CKRFC = 320, |
CKWR = 3, |
CKXPR = CKRFC+5+2; // Clocks per tXPR timeout |
input i_clk, i_reset; |
i_wb_sel, |
// Wishbone outputs |
o_wb_ack, o_wb_stall, o_wb_data, |
o_ddr_reset_n, o_ddr_cke, o_ddr_bus_oe, |
o_ddr_cmd_a, o_ddr_cmd_b, |
o_ddr_data, i_ddr_data); |
// These parameters are not really meant for adjusting from the |
// top level. These are more internal variables, recorded here |
// so that things can be automatically adjusted without much |
// problem. |
parameter CKRP = 3; |
parameter BUSNOW = 4, BUSREG = BUSNOW-1; |
// The commands (above) include (in this order): |
// 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 |
input i_clk, // *MUST* be at 200 MHz for this to work |
i_reset; |
// Wishbone inputs |
input i_wb_cyc, i_wb_stb, i_wb_we; |
input [25:0] i_wb_addr; |
input [31:0] i_wb_data; |
// Wishbone outputs |
output reg o_wb_ack; |
output reg o_wb_stall; |
output reg [31:0] o_wb_data; |
// DDR3 RAM Controller |
output reg o_ddr_reset_n, o_ddr_cke; |
// Control outputs |
output wire 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; |
output reg o_ddr_odt; |
output wire o_ddr_bus_oe; |
// Address outputs |
output wire [13:0] o_ddr_addr; |
output wire [2:0] o_ddr_ba; |
// And the data inputs and outputs |
output reg [31:0] o_ddr_data; |
input [31:0] i_ddr_data; |
input i_wb_cyc, i_wb_stb, i_wb_we; |
input [24:0] i_wb_addr; // Identifies a 64-bit word of interest |
input [63:0] i_wb_data; |
input [7:0] i_wb_sel; |
// Wishbone responses/outputs |
output reg o_wb_ack, o_wb_stall; |
output reg [63:0] o_wb_data; |
// DDR memory command wires |
output reg o_ddr_reset_n, o_ddr_cke, o_ddr_bus_oe; |
// CMDs are: |
// 4 bits of CS, RAS, CAS, WE |
// 3 bits of bank |
// 14 bits of Address |
// 1 bit of DQS (strobe active, or not) |
// 4 bits of mask (one per byte) |
// 1 bit of ODT |
// ---- |
// 27 bits total |
output wire [26:0] o_ddr_cmd_a, o_ddr_cmd_b; |
output reg [63:0] o_ddr_data; |
input [63:0] i_ddr_data; |
|
reg drive_dqs; |
|
// The pending transaction |
reg [31:0] r_data; |
reg r_pending, r_we; |
reg [25:0] r_addr; |
reg [13:0] r_row; |
reg [2:0] r_bank; |
reg [9:0] r_col; |
reg [1:0] r_sub; |
reg r_move; // It was accepted, and can move to next stage |
|
// The pending transaction, one further into the pipeline. This is |
// the stage where the read/write command is actually given to the |
// interface if we haven't stalled. |
reg [31:0] s_data; |
reg s_pending, s_we; // , s_match; |
reg [25:0] s_addr; |
reg [13:0] s_row, s_nxt_row; |
reg [2:0] s_bank, s_nxt_bank; |
reg [9:0] s_col; |
reg [1:0] s_sub; |
|
// Can the pending transaction be satisfied with the current (ongoing) |
// transaction? |
reg m_move, m_match, m_pending, m_we; |
reg [25:0] m_addr; |
reg [13:0] m_row; |
reg [2:0] m_bank; |
reg [9:0] m_col; |
reg [1:0] m_sub; |
|
// Can we preload the next bank? |
reg [13:0] r_nxt_row; |
reg [2:0] r_nxt_bank; |
|
reg need_close_bank, need_close_this_bank, |
last_close_bank, maybe_close_next_bank, |
last_maybe_close, |
need_open_bank, last_open_bank, maybe_open_next_bank, |
last_maybe_open, |
valid_bank, last_valid_bank; |
reg [(`DDR_CMDLEN-1):0] close_bank_cmd, activate_bank_cmd, |
maybe_close_cmd, maybe_open_cmd, rw_cmd; |
reg [1:0] rw_sub; |
reg rw_we; |
|
wire w_this_closing_bank, w_this_opening_bank, |
w_this_maybe_close, w_this_maybe_open, |
w_this_rw_move; |
reg last_closing_bank, last_opening_bank; |
wire w_need_close_this_bank, w_need_open_bank, |
w_r_valid, w_s_valid, w_s_match; |
////////// |
// |
// tWTR = 7.5 |
// tRRD = 7.5 |
// tREFI= 7.8 |
// tFAW = 45 |
// tRTP = 7.5 |
// tCKE = 5.625 |
// tRFC = 160 |
// tRP = 13.5 |
// tRAS = 36 |
// tRCD = 13.5 |
// |
// RESET: |
// 1. Hold o_reset_n = 1'b0; for 200 us, or 40,000 clocks (65536 perhaps?) |
// Hold cke low during this time as well |
// The clock should be free running into the chip during this time |
// Leave command in NOOP state: {cs,ras,cas,we} = 4'h7; |
// ODT must be held low |
// 2. Hold cke low for another 500us, or 100,000 clocks |
// 3. Raise CKE, continue outputting a NOOP for |
// tXPR, tDLLk, and tZQInit |
// 4. Load MRS2, wait tMRD |
// 4. Load MRS3, wait tMRD |
// 4. Load MRS1, wait tMOD |
// Before using the SDRAM, we'll need to program at least 3 of the mode |
// registers, if not all 4. |
// tMOD clocks are required to program the mode registers, during which |
// time the RAM must be idle. |
// Reset Logic |
// |
// NOOP: CS low, RAS, CAS, and WE high |
|
// |
////////// |
// |
// |
// Reset logic should be simple, and is given as follows: |
// note that it depends upon a ROM memory, reset_mem, and an address into that |
// memory: reset_address. Each memory location provides either a "command" to |
203,7 → 139,7
// be set to idle, or whether the command is instead left as it was. |
reg reset_override, reset_ztimer, maintenance_override; |
reg [4:0] reset_address; |
reg [(`DDR_CMDLEN-1):0] reset_cmd, cmd, refresh_cmd, |
reg [(`DDR_CMDLEN-1):0] reset_cmd, cmd_a, cmd_b, refresh_cmd, |
maintenance_cmd; |
reg [24:0] reset_instruction; |
reg [16:0] reset_timer; |
238,9 → 174,15
reset_timer <= reset_instruction[16:0]; |
end |
|
wire [16:0] w_ckXPR = CKXPR, w_ckRST = 4, w_ckRP = 3, |
w_ckRFC_first = CKRFC-2-9; |
wire [16:0] w_ckXPR, w_ckRFC_first; |
wire [13:0] w_MR0, w_MR1, w_MR2; |
assign w_MR0 = 14'h0420; |
assign w_MR1 = 14'h0044; |
assign w_MR2 = 14'h0040; |
assign w_ckXPR = 17'd68; // Table 68, p186 |
assign w_ckRFC_first = 17'd30; // i.e. 64 nCK, or ckREFI |
always @(posedge i_clk) |
// DONE, TIMER, RESET, CKE, |
if (i_reset) |
reset_instruction <= { 4'h4, `DDR_NOOP, 17'd40_000 }; |
else if (reset_ztimer) case(reset_address) // RSTDONE, TIMER, CKE, ?? |
250,60 → 192,36
5'h1: reset_instruction <= { 4'h6, `DDR_NOOP, 17'd100_000 }; |
// 3. Assert CKE, wait minimum of Reset CKE Exit time |
5'h2: reset_instruction <= { 4'h7, `DDR_NOOP, w_ckXPR }; |
// 4. Look MR2. (1CK, no TIMER) |
5'h3: reset_instruction <= { 4'h3, `DDR_MRSET, 3'h2, |
3'h0, 2'b00, 1'b0, 1'b0, 1'b1, 3'b0, 3'b0 }; // MRS2 |
// 3. Wait 4 clocks (tMRD) |
5'h4: reset_instruction <= { 4'h7, `DDR_NOOP, 17'h02 }; |
// 5. Set MR1 |
5'h5: reset_instruction <= { 4'h3, `DDR_MRSET, 3'h1, |
1'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'b0, // DIC set to 2'b00 |
1'b0 }; // MRS1, DLL enable |
// 7. Wait another 4 clocks |
5'h6: reset_instruction <= { 4'h7, `DDR_NOOP, 17'h02 }; |
// 8. Send MRS0 |
5'h7: reset_instruction <= { 4'h3, `DDR_MRSET, 3'h0, |
1'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) |
5'h8: reset_instruction <= { 4'h7, `DDR_NOOP, 17'h0a }; |
// 10. Issue a ZQCL command to start ZQ calibration, A10 is high |
// 4. Set MR2. (4 nCK, no TIMER, but needs a NOOP cycle) |
5'h3: reset_instruction <= { 4'h3, `DDR_MRSET, 3'h2, w_MR2 }; |
5'h4: reset_instruction <= { 4'h3, `DDR_NOOP, 17'h00 }; |
// 5. Set MR1. (4 nCK, no TIMER, but needs a NOOP cycle) |
5'h5: reset_instruction <= { 4'h3, `DDR_MRSET, 3'h1, w_MR1 }; |
5'h6: reset_instruction <= { 4'h3, `DDR_NOOP, 17'h00 }; |
// 6. Set MR0 |
5'h7: reset_instruction <= { 4'h3, `DDR_MRSET, 3'h0, w_MR0 }; |
// 7. Wait 12 clocks |
5'h8: reset_instruction <= { 4'h7, `DDR_NOOP, 17'd10 }; |
// 8. Issue a ZQCL command to start ZQ calibration, A10 is high |
5'h9: reset_instruction <= { 4'h3, `DDR_ZQS, 6'h0, 1'b1, 10'h0}; |
//11.Wait for both tDLLK and tZQinit completed, both are 512 cks |
5'ha: reset_instruction <= { 4'h7, `DDR_NOOP, 17'd512 }; |
//11.Wait for both tDLLK and tZQinit completed, both are |
// 512 cks. Of course, since every one of these commands takes |
// two clocks, we wait for half as many clocks (minus two for |
// our timer logic) |
5'ha: reset_instruction <= { 4'h7, `DDR_NOOP, 17'd254 }; |
// 12. Precharge all command |
5'hb: reset_instruction <= { 4'h3, `DDR_PRECHARGE, 6'h0, 1'b1, 10'h0 }; |
// 13. Wait for the precharge to complete |
5'hc: reset_instruction <= { 4'h7, `DDR_NOOP, w_ckRP }; |
// 13. Wait for the precharge to complete. A count of one, |
// will have us waiting (1+2)*2 or 6 clocks, so we should be |
// good here. |
5'hc: reset_instruction <= { 4'h7, `DDR_NOOP, 17'd1 }; |
// 14. A single Auto Refresh commands |
5'hd: reset_instruction <= { 4'h3, `DDR_REFRESH, 17'h00 }; |
// 15. Wait for the auto refresh to complete |
5'he: reset_instruction <= { 4'h7, `DDR_NOOP, w_ckRFC_first }; |
// Two Auto Refresh commands |
default: |
reset_instruction <={4'hb, `DDR_NOOP, 17'd00_000 }; |
endcase |
// reset_instruction <= reset_mem[reset_address]; |
|
initial reset_address = 5'h0; |
always @(posedge i_clk) |
311,39 → 229,218
reset_address <= 5'h1; |
else if ((reset_ztimer)&&(reset_override)) |
reset_address <= reset_address + 5'h1; |
|
////////// |
// |
// initial reset_mem = |
// 0. !DONE, TIMER,RESET_N=0, CKE=0, CMD = NOOP, TIMER = 200us ( 40,000) |
// 1. !DONE, TIMER,RESET_N=1, CKE=0, CMD = NOOP, TIMER = 500us (100,000) |
// 2. !DONE, TIMER,RESET_N=1, CKE=1, CMD = NOOP, TIMER = (Look me up) |
// 3. !DONE,!TIMER,RESET_N=1, CKE=1, CMD = MODE, MRS |
// 4. !DONE,!TIMER,RESET_N=1, CKE=1, CMD = NOOP, TIMER = tMRS |
// 5. !DONE,!TIMER,RESET_N=1, CKE=1, CMD = MODE, MRS3 |
// 6. !DONE,!TIMER,RESET_N=1, CKE=1, CMD = NOOP, TIMER = tMRS |
// 7. !DONE,!TIMER,RESET_N=1, CKE=1, CMD = MODE, MRS1 |
// 8. !DONE,!TIMER,RESET_N=1, CKE=1, CMD = NOOP, TIMER = tMRS |
// 9. !DONE,!TIMER,RESET_N=1, CKE=1, CMD = MODE, MRS1 |
// 10. !DONE,!TIMER,RESET_N=1, CKE=1, CMD = NOOP, TIMER = tMOD |
// 11. !DONE,!TIMER,RESET_N=1, CKE=1, (Pre-charge all) |
// 12. !DONE,!TIMER,RESET_N=1, CKE=1, (wait) |
// 13. !DONE,!TIMER,RESET_N=1, CKE=1, (Auto-refresh) |
// 14. !DONE,!TIMER,RESET_N=1, CKE=1, (Auto-refresh) |
// 15. !DONE,!TIMER,RESET_N=1, CKE=1, (wait) |
// |
// Refresh Logic |
// |
// |
////////// |
// |
// |
// |
// Okay, let's investigate when we need to do a refresh. Our plan will be to |
// do a single refreshes every tREFI seconds. We will not push off refreshes, |
// nor pull them in--for simplicity. tREFI = 7.8us, but it is a parameter |
// in the number of clocks. In our case, 7.8us / 5ns = 1560 clocks (not nCK!) |
// |
// Note that 160ns are needed between refresh commands (JEDEC, p172), or |
// 32 clocks @200MHz. After this time, no more refreshes will be needed for |
// (1560-32) clocks (@ 200 MHz). |
// |
// This logic is very similar to the refresh logic, both use a memory as a |
// script. |
// |
reg need_refresh; |
reg refresh_ztimer; |
reg [16:0] refresh_counter; |
reg [2:0] refresh_addr; |
reg [23:0] refresh_instruction; |
always @(posedge i_clk) |
if (reset_override) |
refresh_addr <= 3'hf; |
else if (refresh_ztimer) |
refresh_addr <= refresh_addr + 3'h1; |
else if (refresh_instruction[`DDR_RFBEGIN]) |
refresh_addr <= 3'h0; |
|
always @(posedge i_clk) |
if (reset_override) |
begin |
refresh_ztimer <= 1'b1; |
refresh_counter <= 17'd0; |
end else if (!refresh_ztimer) |
begin |
refresh_ztimer <= (refresh_counter == 17'h1); |
refresh_counter <= (refresh_counter - 17'h1); |
end else if (refresh_instruction[`DDR_RFTIMER]) |
begin |
refresh_ztimer <= 1'b0; |
refresh_counter <= refresh_instruction[16:0]; |
end |
|
wire [16:0] w_ckREFI; |
assign w_ckREFI = 17'd1560; // == 6240/4 |
|
wire [16:0] w_ckREFI_left, w_ckRFC_nxt, w_wait_for_idle, |
w_precharge_to_refresh; |
|
// We need to wait for the bus to become idle from whatever state |
// it is in. The difficult time for this measurement is assuming |
// a write was just given. In that case, we need to wait for the |
// write to complete, and then to wait an additional tWR (write |
// recovery time) or 6 nCK clocks from the end of the write. This |
// works out to seven idle bus cycles from the time of the write |
// command, or a count of 5 (7-2). |
assign w_wait_for_idle = 17'd5; // |
assign w_precharge_to_refresh = 17'd1; // = 3-2 |
assign w_ckREFI_left[16:0] = 17'd1560 // The full interval |
-17'd32 // Min what we've already waited |
-w_wait_for_idle |
-w_precharge_to_refresh-17'd12; |
assign w_ckRFC_nxt[16:0] = 17'd32-17'd2; |
|
always @(posedge i_clk) |
if (refresh_ztimer) |
case(refresh_addr)//NEED-REFRESH, HAVE-TIMER, BEGIN(start-over) |
// First, a number of clocks needing no refresh |
3'h0: refresh_instruction <= { 3'h2, `DDR_NOOP, w_ckREFI_left }; |
// Then, we take command of the bus and wait for it to be |
// guaranteed idle |
3'h1: refresh_instruction <= { 3'h6, `DDR_NOOP, w_wait_for_idle }; |
// Once the bus is idle, all commands complete, and a minimum |
// recovery time given, we can issue a precharge all command |
3'h2: refresh_instruction <= { 3'h4, `DDR_PRECHARGE, 17'h0400 }; |
// Now we need to wait tRP = 3 clocks (6 nCK) |
3'h3: refresh_instruction <= { 3'h6, `DDR_NOOP, w_precharge_to_refresh }; |
3'h4: refresh_instruction <= { 3'h4, `DDR_REFRESH, 17'h00 }; |
3'h5: refresh_instruction <= { 3'h6, `DDR_NOOP, w_ckRFC_nxt }; |
default: |
refresh_instruction <= { 3'h1, `DDR_NOOP, 17'h00 }; |
endcase |
|
// Note that we don't need to check if (reset_override) here since |
// refresh_ztimer will always be true if (reset_override)--in other |
// words, it will be true for many, many, clocks--enough for this |
// logic to settle out. |
always @(posedge i_clk) |
if (refresh_ztimer) |
refresh_cmd <= refresh_instruction[20:0]; |
always @(posedge i_clk) |
if (refresh_ztimer) |
need_refresh <= refresh_instruction[`DDR_NEEDREFRESH]; |
|
|
/* |
input i_clk, i_reset; |
// Wishbone inputs |
input i_wb_cyc, i_wb_stb, i_wb_we; |
input [25:0] i_wb_addr; |
input [31:0] i_wb_data; |
// Wishbone outputs |
output reg o_wb_ack; |
output reg o_wb_stall; |
output reg [31:0] o_wb_data; |
// DDR3 RAM Controller |
output reg o_ddr_reset_n, o_ddr_cke; |
// Control outputs |
output wire 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_odt; |
output wire o_ddr_bus_oe; |
// Address outputs |
output wire [13:0] o_ddr_addr; |
output wire [2:0] o_ddr_ba; |
// And the data inputs and outputs |
output reg [31:0] o_ddr_data; |
input [31:0] i_ddr_data; |
*/ |
|
|
reg [1:0] drive_dqs; |
// Our chosen timing doesn't require any more resolution than one |
// bus clock for ODT. (Of course, this really isn't necessary, since |
// we aren't using ODT as per the MRx registers ... but we keep it |
// around in case we change our minds later.) |
reg ddr_odt; |
reg [7:0] ddr_dm; |
|
// The pending transaction |
reg [63:0] r_data; |
reg r_pending, r_we; |
reg [24:0] r_addr; |
reg [13:0] r_row; |
reg [2:0] r_bank; |
reg [9:0] r_col; |
reg r_sub; |
reg [7:0] r_sel; |
|
// The pending transaction, one further into the pipeline. This is |
// the stage where the read/write command is actually given to the |
// interface if we haven't stalled. |
reg [63:0] s_data; |
reg s_pending, s_we; // , s_match; |
reg [24:0] s_addr; |
reg [13:0] s_row, s_nxt_row; |
reg [2:0] s_bank, s_nxt_bank; |
reg [9:0] s_col; |
reg s_sub; |
reg [7:0] s_sel; |
|
// Can the pending transaction be satisfied with the current (ongoing) |
// transaction? |
reg m_move, m_match, m_pending, m_we; |
reg [24:0] m_addr; |
reg [13:0] m_row; |
reg [2:0] m_bank; |
reg [9:0] m_col; |
reg [1:0] m_sub; |
|
// Can we preload the next bank? |
reg [13:0] r_nxt_row; |
reg [2:0] r_nxt_bank; |
|
reg need_close_bank, need_close_this_bank, |
last_close_bank, maybe_close_next_bank, |
last_maybe_close, |
need_open_bank, last_open_bank, maybe_open_next_bank, |
last_maybe_open, |
valid_bank; |
reg [(`DDR_CMDLEN-1):0] close_bank_cmd, activate_bank_cmd, |
maybe_close_cmd, maybe_open_cmd, rw_cmd; |
reg rw_sub; |
reg rw_we; |
|
wire w_this_closing_bank, w_this_opening_bank, |
w_this_maybe_close, w_this_maybe_open, |
w_this_rw_move; |
reg last_closing_bank, last_opening_bank; |
wire w_need_close_this_bank, w_need_open_bank, |
w_r_valid, w_s_valid, w_s_match; |
|
////////// |
// |
// |
// Open Banks |
// |
// |
////////// |
// |
// |
// |
// Let's keep track of any open banks. There are 8 of them to keep track of. |
// |
// A precharge requires 3 clocks at 200MHz to complete, 2 clocks at 100MHz. |
// |
// A precharge requires 3 clocks at 200MHz to complete. |
// An activate also requires 3 clocks at 200MHz to complete. |
// Precharges are not allowed until the maximum of: |
// 2 clocks (200 MHz) after a read command |
// 8 clocks after a write command |
// |
// |
reg need_refresh; |
|
wire w_precharge_all; |
reg banks_are_closing, all_banks_closed; |
reg [3:0] bank_status [0:7]; |
reg [CKRP:0] bank_status [0:7]; |
reg [13:0] bank_address [0:7]; |
reg [3:0] bank_wr_ck [0:7]; // tWTR |
reg bank_wr_ckzro [0:7]; // tWTR |
351,28 → 448,20
reg [7:0] bank_closed; |
|
wire [3:0] write_recycle_clocks; |
assign write_recycle_clocks = CKWR+4+4; |
assign write_recycle_clocks = 4'h8; |
|
initial bank_open = 8'h00; |
initial bank_open = 0; |
initial bank_closed = 8'hff; |
always @(posedge i_clk) |
begin |
bank_status[0] <= { bank_status[0][2:0], bank_status[0][0] }; |
bank_status[1] <= { bank_status[1][2:0], bank_status[1][0] }; |
bank_status[2] <= { bank_status[2][2:0], bank_status[2][0] }; |
bank_status[3] <= { bank_status[3][2:0], bank_status[3][0] }; |
bank_status[4] <= { bank_status[4][2:0], bank_status[4][0] }; |
bank_status[5] <= { bank_status[5][2:0], bank_status[5][0] }; |
bank_status[6] <= { bank_status[6][2:0], bank_status[6][0] }; |
bank_status[7] <= { bank_status[7][2:0], bank_status[7][0] }; |
all_banks_closed <= (bank_status[0][2:0] == 3'b00) |
&&(bank_status[1][2:0] == 3'b00) |
&&(bank_status[2][2:0] == 3'b00) |
&&(bank_status[3][2:0] == 3'b00) |
&&(bank_status[4][2:0] == 3'b00) |
&&(bank_status[5][2:0] == 3'b00) |
&&(bank_status[6][2:0] == 3'b00) |
&&(bank_status[7][2:0] == 3'b00); |
bank_status[0] <= { bank_status[0][(CKRP-1):0], bank_status[0][0] }; |
bank_status[1] <= { bank_status[1][(CKRP-1):0], bank_status[1][0] }; |
bank_status[2] <= { bank_status[2][(CKRP-1):0], bank_status[2][0] }; |
bank_status[3] <= { bank_status[3][(CKRP-1):0], bank_status[3][0] }; |
bank_status[4] <= { bank_status[4][(CKRP-1):0], bank_status[4][0] }; |
bank_status[5] <= { bank_status[5][(CKRP-1):0], bank_status[5][0] }; |
bank_status[6] <= { bank_status[6][(CKRP-1):0], bank_status[6][0] }; |
bank_status[7] <= { bank_status[7][(CKRP-1):0], bank_status[7][0] }; |
|
bank_wr_ck[0] <= (|bank_wr_ck[0])?(bank_wr_ck[0]-4'h1):4'h0; |
bank_wr_ck[1] <= (|bank_wr_ck[1])?(bank_wr_ck[1]-4'h1):4'h0; |
392,23 → 481,23
bank_wr_ckzro[6] <= (bank_wr_ck[6][3:1]==3'b00); |
bank_wr_ckzro[7] <= (bank_wr_ck[7][3:1]==3'b00); |
|
bank_open[0] <= (bank_status[0][1:0] == 2'h3); |
bank_open[1] <= (bank_status[1][1:0] == 2'h3); |
bank_open[2] <= (bank_status[2][1:0] == 2'h3); |
bank_open[3] <= (bank_status[3][1:0] == 2'h3); |
bank_open[4] <= (bank_status[4][1:0] == 2'h3); |
bank_open[5] <= (bank_status[5][1:0] == 2'h3); |
bank_open[6] <= (bank_status[6][1:0] == 2'h3); |
bank_open[7] <= (bank_status[7][1:0] == 2'h3); |
bank_open[0] <= (bank_status[0][(CKRP-2):0] =={(CKRP-1){1'b1}}); |
bank_open[1] <= (bank_status[1][(CKRP-2):0] =={(CKRP-1){1'b1}}); |
bank_open[2] <= (bank_status[2][(CKRP-2):0] =={(CKRP-1){1'b1}}); |
bank_open[3] <= (bank_status[3][(CKRP-2):0] =={(CKRP-1){1'b1}}); |
bank_open[4] <= (bank_status[4][(CKRP-2):0] =={(CKRP-1){1'b1}}); |
bank_open[5] <= (bank_status[5][(CKRP-2):0] =={(CKRP-1){1'b1}}); |
bank_open[6] <= (bank_status[6][(CKRP-2):0] =={(CKRP-1){1'b1}}); |
bank_open[7] <= (bank_status[7][(CKRP-2):0] =={(CKRP-1){1'b1}}); |
|
bank_closed[0] <= (bank_status[0][0] == 1'b0); |
bank_closed[1] <= (bank_status[1][0] == 1'b0); |
bank_closed[2] <= (bank_status[2][0] == 1'b0); |
bank_closed[3] <= (bank_status[3][0] == 1'b0); |
bank_closed[4] <= (bank_status[4][0] == 1'b0); |
bank_closed[5] <= (bank_status[5][0] == 1'b0); |
bank_closed[6] <= (bank_status[6][0] == 1'b0); |
bank_closed[7] <= (bank_status[7][0] == 1'b0); |
bank_closed[0] <= (bank_status[0][(CKRP-3):0] == 0); |
bank_closed[1] <= (bank_status[1][(CKRP-3):0] == 0); |
bank_closed[2] <= (bank_status[2][(CKRP-3):0] == 0); |
bank_closed[3] <= (bank_status[3][(CKRP-3):0] == 0); |
bank_closed[4] <= (bank_status[4][(CKRP-3):0] == 0); |
bank_closed[5] <= (bank_status[5][(CKRP-3):0] == 0); |
bank_closed[6] <= (bank_status[6][(CKRP-3):0] == 0); |
bank_closed[7] <= (bank_status[7][(CKRP-3):0] == 0); |
|
if (w_this_rw_move) |
bank_wr_ck[rw_cmd[16:14]] <= (rw_cmd[`DDR_WEBIT])? 4'h0 |
424,205 → 513,91
bank_status[5][0] <= 1'b0; |
bank_status[6][0] <= 1'b0; |
bank_status[7][0] <= 1'b0; |
banks_are_closing <= 1'b1; |
bank_open <= 0; |
bank_closed <= 8'hff; |
bank_open <= 8'h00; |
end else if (need_close_bank) |
begin |
bank_status[close_bank_cmd[16:14]] |
<= { bank_status[close_bank_cmd[16:14]][2:0], 1'b0 }; |
<= { bank_status[close_bank_cmd[16:14]][(CKRP-1):0], 1'b0 }; |
bank_open[close_bank_cmd[16:14]] <= 1'b0; |
// bank_status[close_bank_cmd[16:14]][0] <= 1'b0; |
end else if (need_open_bank) |
begin |
bank_status[activate_bank_cmd[16:14]] |
<= { bank_status[activate_bank_cmd[16:14]][2:0], 1'b1 }; |
// bank_status[activate_bank_cmd[16:14]][0] <= 1'b1; |
all_banks_closed <= 1'b0; |
banks_are_closing <= 1'b0; |
<= { bank_status[activate_bank_cmd[16:14]][(CKRP-1):0], 1'b1 }; |
bank_closed[activate_bank_cmd[16:14]] <= 1'b0; |
end else if (valid_bank) |
; |
; // Read/write command was issued. This neither opens |
// nor closes any banks, and hence it needs no logic |
// here |
else if (maybe_close_next_bank) |
begin |
bank_status[maybe_close_cmd[16:14]] |
<= { bank_status[maybe_close_cmd[16:14]][2:0], 1'b0 }; |
<= { bank_status[maybe_close_cmd[16:14]][(CKRP-1):0], 1'b0 }; |
bank_open[maybe_close_cmd[16:14]] <= 1'b0; |
end else if (maybe_open_next_bank) |
begin |
bank_status[maybe_open_cmd[16:14]] |
<= { bank_status[maybe_open_cmd[16:14]][2:0], 1'b1 }; |
// bank_status[activate_bank_cmd[16:14]][0] <= 1'b1; |
all_banks_closed <= 1'b0; |
banks_are_closing <= 1'b0; |
<= { bank_status[maybe_open_cmd[16:14]][(CKRP-1):0], 1'b1 }; |
bank_closed[maybe_open_cmd[16:14]] <= 1'b0; |
end |
end |
|
always @(posedge i_clk) |
// if (cmd[22:19] == `DDR_ACTIVATE) |
if (w_this_opening_bank) |
bank_address[activate_bank_cmd[16:14]] |
<= activate_bank_cmd[13:0]; |
else if (!w_this_maybe_open) |
else if (w_this_maybe_open) |
bank_address[maybe_open_cmd[16:14]] |
<= maybe_open_cmd[13:0]; |
|
|
////////// |
// |
// |
// Okay, let's investigate when we need to do a refresh. Our plan will be to |
// do 4 refreshes every tREFI*4 seconds. tREFI = 7.8us, but its a parameter |
// in the number of clocks so that we can handle both 100MHz and 200MHz clocks. |
// Data BUS information |
// |
// Note that 160ns are needed between refresh commands (JEDEC, p172), or |
// 320 clocks @200MHz, or equivalently 160 clocks @100MHz. Thus to issue 4 |
// of these refresh cycles will require 4*320=1280 clocks@200 MHz. After this |
// time, no more refreshes will be needed for 6240 clocks. |
// |
// Let's think this through: |
// REFRESH_COST = (n*(320)+24)/(n*1560) |
// |
////////// |
// |
// |
reg refresh_ztimer; |
reg [16:0] refresh_counter; |
reg [2:0] refresh_addr; |
reg [23:0] refresh_instruction; |
always @(posedge i_clk) |
if (reset_override) |
refresh_addr <= 3'hf; |
else if (refresh_ztimer) |
refresh_addr <= refresh_addr + 3'h1; |
else if (refresh_instruction[`DDR_RFBEGIN]) |
refresh_addr <= 3'h0; |
|
always @(posedge i_clk) |
if (reset_override) |
begin |
refresh_ztimer <= 1'b1; |
refresh_counter <= 17'd0; |
end else if (!refresh_ztimer) |
begin |
refresh_ztimer <= (refresh_counter == 17'h1); |
refresh_counter <= (refresh_counter - 17'h1); |
end else if (refresh_instruction[`DDR_RFTIMER]) |
begin |
refresh_ztimer <= 1'b0; |
refresh_counter <= refresh_instruction[16:0]; |
end |
|
`ifdef QUADRUPLE_REFRESH |
// REFI4 = 13'd6240 |
wire [16:0] w_ckREFIn, w_ckREFRst; |
assign w_ckREFIn[(CKRBITS-1): 0] = CKREFI4-5*CKRFC-2-10; |
assign w_ckREFIn[ 16:(CKRBITS)] = 0; |
assign w_ckREFRst = CKRFC-2-12; |
|
always @(posedge i_clk) |
if (refresh_ztimer) |
case(refresh_addr)//NEED-RFC, HAVE-TIMER, |
4'h0: refresh_instruction <= { 3'h2, `DDR_NOOP, w_ckREFIn }; |
// 17'd10 = time to complete write, plus write recovery time |
// minus two (cause we can't count zero or one) |
// = WL+4+tWR-2 = 10 |
// = 5+4+3-2 = 10 |
4'h1: refresh_instruction <= { 3'h6, `DDR_NOOP, 17'd10 }; |
4'h2: refresh_instruction <= { 3'h4, `DDR_PRECHARGE, 17'h0400 }; |
4'h3: refresh_instruction <= { 3'h6, `DDR_NOOP, 17'd2 }; |
4'h4: refresh_instruction <= { 3'h4, `DDR_REFRESH, 17'h00 }; |
4'h5: refresh_instruction <= { 3'h6, `DDR_NOOP, w_ckRFC }; |
4'h6: refresh_instruction <= { 3'h4, `DDR_REFRESH, 17'h00 }; |
4'h7: refresh_instruction <= { 3'h6, `DDR_NOOP, w_ckRFC }; |
4'h8: refresh_instruction <= { 3'h4, `DDR_REFRESH, 17'h00 }; |
4'h9: refresh_instruction <= { 3'h6, `DDR_NOOP, w_ckRFC }; |
4'ha: refresh_instruction <= { 3'h4, `DDR_REFRESH, 17'h00 }; |
4'hb: refresh_instruction <= { 3'h6, `DDR_NOOP, w_ckRFC }; |
4'hc: refresh_instruction <= { 3'h2, `DDR_NOOP, w_ckREFRst }; |
default: |
refresh_instruction <= { 3'h1, `DDR_NOOP, 17'h00 }; |
endcase |
`else |
wire [16:0] w_ckREFI_left, w_ckRFC_nxt; |
assign w_ckREFI_left[12:0] = CKREFI-CKRFC-2-19; |
assign w_ckREFI_left[16:13] = 0; |
assign w_ckRFC_nxt[8:0] = CKRFC+9'h2; |
assign w_ckRFC_nxt[16:9] = 0; |
|
always @(posedge i_clk) |
if (refresh_ztimer) |
case(refresh_addr)//NEED-REFRESH, HAVE-TIMER, BEGIN(start-over) |
3'h0: refresh_instruction <= { 3'h2, `DDR_NOOP, w_ckREFI_left }; |
3'h1: refresh_instruction <= { 3'h6, `DDR_NOOP, 17'd10 }; |
3'h2: refresh_instruction <= { 3'h4, `DDR_PRECHARGE, 17'h0400 }; |
3'h3: refresh_instruction <= { 3'h6, `DDR_NOOP, 17'd2 }; |
3'h4: refresh_instruction <= { 3'h4, `DDR_REFRESH, 17'h00 }; |
3'h5: refresh_instruction <= { 3'h6, `DDR_NOOP, w_ckRFC_nxt }; |
default: |
refresh_instruction <= { 3'h1, `DDR_NOOP, 17'h00 }; |
endcase |
`endif |
|
always @(posedge i_clk) |
if (reset_override) |
refresh_cmd <= { `DDR_NOOP, w_ckREFI_left }; |
else if (refresh_ztimer) |
refresh_cmd <= refresh_instruction[20:0]; |
always @(posedge i_clk) |
if (reset_override) |
need_refresh <= 1'b0; |
else if (refresh_ztimer) |
need_refresh <= refresh_instruction[`DDR_NEEDREFRESH]; |
|
|
// Our purpose here is to keep track of when the data bus will be |
// active. This is separate from the FIFO which will contain the |
// data to be placed on the bus (when so placed), in that this is |
// a group of shift registers--every position has a location in time, |
// and time always moves forward. The FIFO, on the other hand, only |
// moves forward when data moves onto the bus. |
// |
// |
// Let's track: when will our bus be active? When will we be reading or |
// writing? |
// |
// |
reg [`BUSNOW:0] bus_active, bus_read, bus_new, bus_ack; |
reg [1:0] bus_subaddr [`BUSNOW:0]; |
|
reg [BUSNOW:0] bus_active, bus_read, bus_new, bus_ack; |
reg [BUSNOW:0] bus_subaddr, bus_odt; |
initial bus_active = 0; |
initial bus_ack = 0; |
always @(posedge i_clk) |
begin |
bus_active[`BUSNOW:0] <= { bus_active[(`BUSNOW-1):0], 1'b0 }; |
bus_read[`BUSNOW:0] <= { bus_read[(`BUSNOW-1):0], 1'b0 }; // Drive the d-bus? |
bus_active[BUSNOW:0] <= { bus_active[(BUSNOW-1):0], 1'b0 }; |
// Drive the d-bus? |
bus_read[BUSNOW:0] <= { bus_read[(BUSNOW-1):0], 1'b0 }; |
// Is this a new command? i.e., the start of a transaction? |
bus_new[`BUSNOW:0] <= { bus_new[(`BUSNOW-1):0], 1'b0 }; |
bus_new[BUSNOW:0] <= { bus_new[(BUSNOW-1):0], 1'b0 }; |
bus_odt[BUSNOW:0] <= { bus_odt[(BUSNOW-1):0], 1'b0 }; |
// Will this position on the bus get a wishbone acknowledgement? |
bus_ack[`BUSNOW:0] <= { bus_ack[(`BUSNOW-1):0], 1'b0 }; |
//bus_mask[8:0] <= { bus_mask[7:0], 1'b1 }; // Write this value? |
bus_subaddr[8] <= bus_subaddr[7]; |
bus_subaddr[7] <= bus_subaddr[6]; |
bus_subaddr[6] <= bus_subaddr[5]; |
bus_subaddr[5] <= bus_subaddr[4]; |
bus_subaddr[4] <= bus_subaddr[3]; |
bus_subaddr[3] <= bus_subaddr[2]; |
bus_subaddr[2] <= bus_subaddr[1]; |
bus_subaddr[1] <= bus_subaddr[0]; |
bus_subaddr[0] <= 2'h3; |
bus_ack[BUSNOW:0] <= { bus_ack[(BUSNOW-1):0], 1'b0 }; |
// |
bus_subaddr[BUSNOW:0] <= { bus_subaddr[(BUSNOW-1):0], 1'b1 }; |
|
bus_ack[5] <= (bus_ack[4])&& |
((bus_subaddr[5] != bus_subaddr[4]) |
||(bus_new[4])); |
if (w_this_rw_move) |
begin |
bus_active[3:0]<= 4'hf; // Once per clock |
bus_subaddr[3] <= 2'h0; |
bus_subaddr[2] <= 2'h1; |
bus_subaddr[1] <= 2'h2; |
bus_active[1:0]<= 2'h3; // Data transfers in two clocks |
bus_subaddr[1] <= 1'h0; |
bus_new[{ 2'b0, rw_sub }] <= 1'b1; |
bus_ack[3:0] <= 4'h0; |
bus_ack[1:0] <= 2'h0; |
bus_ack[{ 2'b0, rw_sub }] <= 1'b1; |
|
bus_read[3:0] <= (rw_we)? 4'h0:4'hf; |
bus_read[1:0] <= (rw_we)? 2'h0:2'h3; |
bus_odt[3:0]<= (rw_we)? 4'he:4'h0; // Data transfers in 2 clks |
end else if ((s_pending)&&(!pipe_stall)) |
begin |
if (bus_subaddr[3] == s_sub) |
bus_ack[4] <= 1'b1; |
if (bus_subaddr[2] == s_sub) |
bus_ack[3] <= 1'b1; |
if (bus_subaddr[1] == s_sub) |
bus_ack[2] <= 1'b1; |
if (bus_subaddr[0] == s_sub) |
632,8 → 607,12
|
// Need to set o_wb_dqs high one clock prior to any read. |
always @(posedge i_clk) |
drive_dqs <= (|bus_active[`BUSREG:(`BUSREG-1)]) |
&&(~(|bus_read[`BUSREG:(`BUSREG-1)])); |
begin |
drive_dqs[1] <= (bus_active[(BUSREG)]) |
&&(!bus_read[(BUSREG)]); |
drive_dqs[0] <= (bus_active[BUSREG:(BUSREG-1)] != 2'b00) |
&&(bus_read[BUSREG:(BUSREG-1)] == 2'b00); |
end |
|
// |
// |
646,25 → 625,21
pre_valid <= 1'b0; |
else if (need_refresh) |
pre_valid <= 1'b0; |
else if (w_this_rw_move) |
pre_valid <= 1'b0; |
else if (bus_active[0]) |
pre_valid <= 1'b0; |
else |
pre_valid <= 1'b1; |
|
assign w_r_valid = (pre_valid)&&(r_pending) |
&&(bank_status[r_bank][1]) |
&&(bank_status[r_bank][(CKRP-2)]) |
&&(bank_address[r_bank]==r_row) |
&&((r_we)||(bank_wr_ckzro[r_bank])); |
assign w_s_valid = (pre_valid)&&(s_pending) |
&&(bank_status[s_bank][1]) |
&&(bank_status[s_bank][(CKRP-2)]) |
&&(bank_address[s_bank]==s_row) |
&&((s_we)||(bank_wr_ckzro[s_bank])); |
assign w_s_match = (s_pending)&&(r_pending)&&(r_we == s_we) |
&&(r_row == s_row)&&(r_bank == s_bank) |
&&(r_col == s_col) |
&&(r_sub > s_sub); |
&&(r_sub)&&(!s_sub); |
|
reg pipe_stall; |
always @(posedge i_clk) |
678,8 → 653,8
pipe_stall <= (r_pending)&&(((!w_r_valid)||(valid_bank))&&(!w_s_match)); |
o_wb_stall <= (r_pending)&&(((!w_r_valid)||(valid_bank))&&(!w_s_match)); |
end else begin // if (pipe_stall) |
pipe_stall <= (s_pending)&&((!w_s_valid)||(valid_bank)||(r_move)||(last_valid_bank)); |
o_wb_stall <= (s_pending)&&((!w_s_valid)||(valid_bank)||(r_move)||(last_valid_bank)); |
pipe_stall <= (s_pending)&&((!w_s_valid)||(valid_bank)); |
o_wb_stall <= (s_pending)&&((!w_s_valid)||(valid_bank)); |
end |
if (need_refresh) |
o_wb_stall <= 1'b1; |
689,14 → 664,22
r_we <= i_wb_we; |
r_addr <= i_wb_addr; |
r_data <= i_wb_data; |
r_row <= i_wb_addr[25:12]; |
r_bank <= i_wb_addr[11:9]; |
r_col <= { i_wb_addr[8:2], 3'b000 }; // 9:2 |
r_sub <= i_wb_addr[1:0]; |
r_row <= i_wb_addr[24:11]; // 14 bits row address |
r_bank <= i_wb_addr[10:8]; |
r_col <= { i_wb_addr[7:1], 3'b000 }; // 10 bits Caddr |
r_sub <= i_wb_addr[0]; // Select which 64-bit word |
r_sel <= i_wb_sel; |
|
// i_wb_addr[0] is the 8-bit byte selector of 16-bits (ignored) |
// i_wb_addr[1] is the 16-bit half-word selector of 32-bits (ignored) |
// i_wb_addr[2] is the 32-bit word selector of 64-bits (ignored) |
// i_wb_addr[3] is the 64-bit long word selector of 128-bits |
|
// pre-emptive work |
r_nxt_row <= (i_wb_addr[11:9]==3'h7)?i_wb_addr[25:12]+14'h1:i_wb_addr[25:12]; |
r_nxt_bank <= i_wb_addr[11:9]+3'h1; |
r_nxt_row <= (i_wb_addr[10:8]==3'h7) |
? (i_wb_addr[24:11]+14'h1) |
: i_wb_addr[24:11]; |
r_nxt_bank <= i_wb_addr[10:8]+3'h1; |
end |
|
if (~pipe_stall) |
709,12 → 692,11
s_bank <= r_bank; |
s_col <= r_col; |
s_sub <= r_sub; |
s_sel <= (r_we)?(~r_sel):8'h00; |
|
// pre-emptive work |
s_nxt_row <= r_nxt_row; |
s_nxt_bank <= r_nxt_bank; |
|
// s_match <= w_s_match; |
end |
end |
|
760,9 → 742,8
|
|
valid_bank <= ((w_r_valid)||((pipe_stall)&&(w_s_valid))) |
&&(!last_valid_bank)&&(!r_move) |
// &&(!last_valid_bank)&&(!r_move) |
&&(!w_this_rw_move); |
last_valid_bank <= r_move; |
|
if ((s_pending)&&(pipe_stall)) |
rw_cmd[`DDR_CSBIT:`DDR_WEBIT] <= (s_we)?`DDR_WRITE:`DDR_READ; |
775,9 → 756,9
else |
rw_cmd[`DDR_WEBIT-1:0] <= { r_bank, 3'h0, 1'b0, r_col }; |
if ((s_pending)&&(pipe_stall)) |
rw_sub <= 2'b11 - s_sub; |
rw_sub <= 1'b1 - s_sub; |
else |
rw_sub <= 2'b11 - r_sub; |
rw_sub <= 1'b1 - r_sub; |
if ((s_pending)&&(pipe_stall)) |
rw_we <= s_we; |
else |
835,108 → 816,99
last_closing_bank <= 1'b0; |
last_maybe_open <= 1'b0; |
last_maybe_close <= 1'b0; |
r_move <= 1'b0; |
if (maintenance_override) // Command from either reset or |
cmd <= maintenance_cmd; // refresh logic |
else if (need_close_bank) |
cmd_a <= { `DDR_NOOP, 17'h00 }; |
cmd_b <= { `DDR_NOOP, rw_cmd[(`DDR_WEBIT-1):0] }; |
|
if (maintenance_override) |
begin // Command from either reset or refresh logic |
cmd_a <= maintenance_cmd; |
// cmd_b <= { `DDR_NOOP, ... |
end else if (need_close_bank) |
begin |
cmd <= close_bank_cmd; |
cmd_a <= close_bank_cmd; |
// cmd_b <= { `DDR_NOOP, ...} |
last_closing_bank <= 1'b1; |
end else if (need_open_bank) |
begin |
cmd <= activate_bank_cmd; |
cmd_a <= activate_bank_cmd; |
// cmd_b <={`DDR_NOOP, ...} |
last_opening_bank <= 1'b1; |
end else if (valid_bank) |
begin |
cmd <= rw_cmd; |
r_move <= 1'b1; |
cmd_a <= {(rw_cmd[(`DDR_WEBIT)])?`DDR_READ:`DDR_NOOP, |
rw_cmd[(`DDR_WEBIT-1):0] }; |
cmd_b <= {(rw_cmd[(`DDR_WEBIT)])?`DDR_NOOP:`DDR_WRITE, |
rw_cmd[(`DDR_WEBIT-1):0] }; |
end else if (maybe_close_next_bank) |
begin |
cmd <= maybe_close_cmd; |
cmd_a <= maybe_close_cmd; |
// cmd_b <= {`DDR_NOOP, ... } |
last_maybe_close <= 1'b1; |
end else if (maybe_open_next_bank) |
begin |
cmd <= maybe_open_cmd; |
cmd_a <= maybe_open_cmd; |
// cmd_b <= {`DDR_NOOP, ... } |
last_maybe_open <= 1'b1; |
end else |
cmd <= { `DDR_NOOP, rw_cmd[(`DDR_WEBIT-1):0] }; |
cmd_a <= { `DDR_NOOP, rw_cmd[(`DDR_WEBIT-1):0] }; |
end |
|
`define LGFIFOLN 4 |
`define FIFOLEN 16 |
reg [(`LGFIFOLN-1):0] bus_fifo_head, bus_fifo_tail; |
reg [31:0] bus_fifo_data [0:(`FIFOLEN-1)]; |
reg [1:0] bus_fifo_sub [0:(`FIFOLEN-1)]; |
reg [63:0] bus_fifo_data [0:(`FIFOLEN-1)]; |
reg [7:0] bus_fifo_sel [0:(`FIFOLEN-1)]; |
reg bus_fifo_sub [0:(`FIFOLEN-1)]; |
reg bus_fifo_new [0:(`FIFOLEN-1)]; |
reg pre_ack; |
|
// The bus R/W FIFO |
wire w_bus_fifo_read_next_transaction; |
assign w_bus_fifo_read_next_transaction = (bus_ack[`BUSREG]); |
assign w_bus_fifo_read_next_transaction = (bus_ack[BUSREG]); |
always @(posedge i_clk) |
begin |
pre_ack <= 1'b0; |
o_ddr_dm <= 1'b0; |
if (reset_override) |
begin |
bus_fifo_head <= {(`LGFIFOLN){1'b0}}; |
bus_fifo_tail <= {(`LGFIFOLN){1'b0}}; |
o_ddr_dm <= 1'b0; |
end else begin |
if ((s_pending)&&(!pipe_stall)) |
bus_fifo_head <= bus_fifo_head + 1'b1; |
|
o_ddr_dm <= (bus_active[`BUSREG])&&(!bus_read[`BUSREG]); |
if (w_bus_fifo_read_next_transaction) |
begin |
bus_fifo_tail <= bus_fifo_tail + 1'b1; |
pre_ack <= 1'b1; |
o_ddr_dm <= 1'b0; |
end |
end |
bus_fifo_data[bus_fifo_head] <= s_data; |
bus_fifo_sub[bus_fifo_head] <= s_sub; |
bus_fifo_new[bus_fifo_head] <= w_this_rw_move; |
|
// |
// if ((s_pending)&&(!pipe_stall)&&(!nxt_valid)) |
// nxt_fifo_data <= s_data; |
// nxt_fifo_sub <= s_sub; |
// nxt_fifo_new <= w_this_rw_move; |
// nxt_valid <= 1'b1; |
// bus_fifo_head <= bus_fifo_head+1; |
// bus_fifo_tail <= bus_fifo_tail+1; |
// else if (w_bus_fifo_read_next_transaction) |
// nxt_fifo_data <= bus_fifo_data[bus_fifo_tail] |
// nxt_fifo_sub <= bus_fifo_data[bus_fifo_tail] |
// nxt_fifo_new <= bus_fifo_data[bus_fifo_tail] |
// nxt_valid <= (bus_fifo_tail+1 == bus_fifo_head); |
// |
// if ((!valid)||(w_bus_fifo_next_read_transaction)) |
// nxt_ <= bus_fifo_x |
bus_fifo_sel[bus_fifo_head] <= s_sel; |
end |
|
|
assign o_ddr_cs_n = cmd[`DDR_CSBIT]; |
assign o_ddr_ras_n = cmd[`DDR_RASBIT]; |
assign o_ddr_cas_n = cmd[`DDR_CASBIT]; |
assign o_ddr_we_n = cmd[`DDR_WEBIT]; |
assign o_ddr_dqs = drive_dqs; |
assign o_ddr_addr = cmd[(`DDR_ADDR_BITS-1):0]; |
assign o_ddr_ba = cmd[(`DDR_BABITS+`DDR_ADDR_BITS-1):`DDR_ADDR_BITS]; |
always @(posedge i_clk) |
o_ddr_data <= bus_fifo_data[bus_fifo_tail]; |
assign w_precharge_all = (cmd[`DDR_CSBIT:`DDR_WEBIT]==`DDR_PRECHARGE) |
&&(o_ddr_addr[10]); // 5 bits |
always @(posedge i_clk) |
ddr_dm <= (bus_ack[BUSREG])? bus_fifo_sel[bus_fifo_tail] |
: ((!bus_read[BUSREG])? 8'hff: 8'h00); |
always @(posedge i_clk) |
o_ddr_bus_oe <= (bus_active[BUSREG])&&(!bus_read[BUSREG]); |
|
assign o_ddr_bus_oe = drive_dqs; // ~bus_read[`BUSNOW]; |
// First, or left, command |
assign o_ddr_cmd_a = { cmd_a, drive_dqs[1], ddr_dm[7:4], ddr_odt }; |
// Second, or right, command of two |
assign o_ddr_cmd_b = { cmd_b, drive_dqs[0], ddr_dm[3:0], ddr_odt }; |
|
assign w_precharge_all = (cmd_a[`DDR_CSBIT:`DDR_WEBIT]==`DDR_PRECHARGE) |
&&(cmd_a[10]); |
|
// ODT must be in high impedence while reset_n=0, then it can be set |
// to low or high. As per spec, ODT = 0 during reads |
always @(posedge i_clk) |
o_ddr_odt <= (bus_active[`BUSREG-3])&&(!bus_read[`BUSREG-3]) |
||(bus_active[`BUSREG-4])&&(!bus_read[`BUSREG-4]) |
||((w_this_rw_move)&&(rw_we)); |
ddr_odt <= bus_odt[BUSREG]; |
|
always @(posedge i_clk) |
o_wb_ack <= pre_ack; |