URL
https://opencores.org/ocsvn/wbddr3/wbddr3/trunk
Subversion Repositories wbddr3
[/] [wbddr3/] [trunk/] [bench/] [cpp/] [ddrsdramsim.cpp] - Rev 18
Compare with Previous | Blame | View Log
//////////////////////////////////////////////////////////////////////////////// // // 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> #include "ddrsdramsim.h" #define PREFIX "DDR3-SDRAM" const unsigned ckRESET = 64000, // 200us @ 320MHz ckCKE = 160000, // 500us @ 320MHz ckCL = 5, ckCWL = 5, ckRP = 5, ckWR = 5, ckRAS = 12, ckRC = 16, ckMRD = 4, ckMOD = 12, ckZQinit = 512, ckODT = ckCWL-2, ckXPR = 56, // Exit reset clocks from CKE HIGH to valid command ckRFC = 52, // Clocks from refresh to activate ckREFI = 2496, // 7.8us @ 320MHz 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)), // Don't pull in any refreshes ... nREF = 1; BANKINFO::BANKINFO(void) { m_state = 0; m_row = 0; m_wcounter = 0; m_min_time_before_precharge=0; } void BANKINFO::tick(int cmd, unsigned addr) { if (m_wcounter) m_wcounter--; switch(cmd) { case DDR_REFRESH: assert(m_state == 0); break; case DDR_PRECHARGE: // 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: // 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); } if (m_min_time_before_activate != 0) { 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&((1<<ckRP)-1))!=0)&&((addr&0x7fff) != m_row)) { printf(PREFIX "::BANK-FAIL: Attempt to Activate an already active bank without closing it first (m_state = %x)\n", m_state); // assert((m_state&((1<<ckRP)-1))==0); } else if((m_state&((1<<ckRP)-1)) != 0) { printf(PREFIX "::DOUBLE-ACTIVATE!!\n"); } /* if (m_wcounter != 0) { printf("BANK-FAIL: ACTIVATE too soon after write (wcounter = %d)\n", m_wcounter); assert(m_wcounter == 0); } if (m_min_time_before_activate!=0) { 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(PREFIX "::BANK -- Setting address to %04x\n", m_row); m_min_time_before_precharge = ckRAS; m_min_time_before_activate = ckRC; break; case DDR_READ: case DDR_WRITE: if (DDR_READ) assert(m_wcounter == 0); else m_wcounter = 3+4+4; 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) m_min_time_before_activate--; break; case DDR_ZQS: assert((m_state&((1<<ckRP)-1)) == 0); if (m_min_time_before_precharge) m_min_time_before_precharge--; if (m_min_time_before_activate) m_min_time_before_activate--; break; case DDR_NOOP: // NO BREAK: Fall through. On a NOOP we do the same // as if the chip were de-selected. default: m_state <<= 1; m_state |= (m_state&2)>>1; m_state &= ((1<<ckRP)-1); // Else the chip was de-selected, and we only need // tick time away -- as in a NOOP. if (m_min_time_before_precharge) m_min_time_before_precharge--; if (m_min_time_before_activate) m_min_time_before_activate--; 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; assert(NTIMESLOTS > ckCL+3); m_bus = new BUSTIMESLOT[NTIMESLOTS]; for(int i=0; i<NTIMESLOTS; i++) m_bus[i].m_used = 0; for(int i=0; i<NTIMESLOTS; i++) m_bus[i].m_rtt = 0; m_busloc = 0; } 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) { BUSTIMESLOT *ts, *nxtts; 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) { // 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 >= (int)ckRESET); m_reset_counts = 0; m_reset_state = 1; } break; case 1: m_reset_counts++; if (cke) { assert(m_reset_counts >= (int)ckCKE); m_reset_counts = 0; m_reset_state = 2; } break; case 2: m_reset_counts++; assert(cke); if ((cmd != DDR_NOOP)&&(cmd&DDR_DESELECT)==0) { assert(m_reset_counts >= (int)ckXPR); m_reset_counts = 0; m_reset_state = 3; assert(cmd == DDR_MRSET); // Set MR2 assert(ba == 2); 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)&&(cmd&DDR_DESELECT)==0) { 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 == DDR_MR1); } break; case 4: m_reset_counts++; assert(cke); if ((cmd != DDR_NOOP)&&(cmd&DDR_DESELECT)==0) { printf(PREFIX "::RESET-CMD[4]: %d:%08x[%d]@0x%04x\n", cmd, m_reset_counts, ba, addr); 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 == DDR_MR0); } break; case 5: m_reset_counts++; assert(cke); if ((cmd != DDR_NOOP)&&(cmd&DDR_DESELECT)==0) { printf(PREFIX "::RESET-CMD[5]: %d:%08x[%d]@0x%04x\n", cmd, m_reset_counts, ba, addr); assert(m_reset_counts >= (int)ckMOD); m_reset_counts = 0; m_reset_state = 6; assert(cmd == DDR_ZQS); assert(addr == 0x400); } break; case 6: m_reset_counts++; assert(cke); if ((cmd != DDR_NOOP)&&(cmd&DDR_DESELECT)==0) { printf(PREFIX "::RESET-CMD[6]: %d:%08x[%d]@0x%04x\n", cmd, m_reset_counts, ba, addr); assert(m_reset_counts >= (int)ckZQinit); m_reset_counts = 0; m_reset_state = 7; assert(cmd == DDR_PRECHARGE); assert(addr == 0x400); } break; case 7: m_reset_counts++; assert(cke); if ((cmd != DDR_NOOP)&&(cmd&DDR_DESELECT)==0) { printf(PREFIX "::RESET-CMD[7]: %d:%08x[%d]@0x%04x\n", cmd, m_reset_counts, ba, addr); assert(m_reset_counts >= (int)ckRP); m_reset_counts = 0; m_reset_state = 8; assert(cmd == DDR_REFRESH); m_clocks_since_refresh = 0; } break; case 8: m_reset_counts++; assert(cke); assert((cmd == DDR_NOOP)||(cmd&DDR_DESELECT)); if (m_reset_counts > (int)ckRFC) { m_reset_state = 16; printf(PREFIX ": Leaving reset state\n"); } break; default: break; } gbl_state = m_reset_state; gbl_counts= m_reset_counts; m_nrefresh_issued = nREF; m_clocks_since_refresh++; for(int i=0; i<NBANKS; i++) m_bank[i].tick(cmd, 0); } else if (!cke) { assert(0&&"Clock not enabled!"); } else if ((cmd == DDR_REFRESH)||(m_nrefresh_issued < (int)nREF)) { if (DDR_REFRESH == cmd) { m_clocks_since_refresh = 0; if (m_nrefresh_issued >= (int)nREF) m_nrefresh_issued = 1; else m_nrefresh_issued++; } else { m_clocks_since_refresh++; assert(DDR_NOOP == cmd); } for(int i=0; i<NBANKS; i++) m_bank[i].tick(cmd,0); if (m_nrefresh_issued == nREF) printf(PREFIX "::Refresh cycle complete\n"); } else { // In operational mode!! m_clocks_since_refresh++; 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"); 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); m_clocks_since_refresh = 0; assert(0 && "Internal err: Refresh should be handled above"); break; 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) m_bank[i].tick(DDR_NOOP,0); } break; case DDR_ACTIVATE: 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++) if (i!=ba) m_bank[i].tick(DDR_NOOP,0); break; case DDR_WRITE: { // This SIM doesn't handle out of order writes assert((addr&7)==0); m_bank[ba].tick(DDR_WRITE, addr); for(int i=0; i<NBANKS; i++) if (i!=ba)m_bank[i].tick(DDR_NOOP,addr); unsigned caddr = m_bank[ba].m_row; caddr <<= 3; caddr |= ba; caddr <<= 10; caddr |= addr; caddr &= ~7; caddr >>= 1; BUSTIMESLOT *tp; 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); tp->m_addr = caddr ; tp->m_used = 1; tp->m_read = 0; tp = &m_bus[(offset+1)&(NTIMESLOTS-1)]; tp->m_addr = caddr+1; tp->m_used = 1; tp->m_read = 0; tp = &m_bus[(offset+2)&(NTIMESLOTS-1)]; tp->m_addr = caddr+2; tp->m_used = 1; tp->m_read = 0; tp = &m_bus[(offset+3)&(NTIMESLOTS-1)]; tp->m_addr = caddr+3; tp->m_used = 1; tp->m_read = 0; } break; case DDR_READ: { // This SIM doesn't handle out of order reads assert((addr&7)==0); m_bank[ba].tick(DDR_READ, addr); for(int i=0; i<NBANKS; i++) if (i!=ba)m_bank[i].tick(DDR_NOOP,addr); unsigned caddr = m_bank[ba].m_row; caddr <<= 3; caddr |= ba; caddr <<= 10; caddr |= addr; caddr &= ~7; 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]; tp->m_addr = caddr; tp->m_used = 1; tp->m_read = 1; tp = &m_bus[(offset+1)&(NTIMESLOTS-1)]; tp->m_data = m_mem[caddr+1]; tp->m_addr = caddr+1; tp->m_used = 1; tp->m_read = 1; tp = &m_bus[(offset+2)&(NTIMESLOTS-1)]; tp->m_data = m_mem[caddr+2]; tp->m_addr = caddr+2; tp->m_used = 1; tp->m_read = 1; tp = &m_bus[(offset+3)&(NTIMESLOTS-1)]; tp->m_data = m_mem[caddr+3]; tp->m_addr = caddr+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: default: // We are deselected for(int i=0; i<NBANKS; i++) m_bank[i].tick(DDR_NOOP,addr); break; } if (false) { bool flag = false; for(int i=0; i<5; i++) { int bl = (m_busloc+1+i)&(NTIMESLOTS-1); nxtts = &m_bus[bl]; if (nxtts->m_used) { flag = true; break; } } if (flag) { 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]; printf("BUS[%2d] ", bl); if (nxtts->m_used) printf(" USED"); if (nxtts->m_read) printf(" READ"); if (nxtts->m_rtt) printf(" RTT"); printf("\n"); }} } ts = &m_bus[(m_busloc+1)&(NTIMESLOTS-1)]; 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) // assert(!m_last_rtt); } m_busloc = (m_busloc+1)&(NTIMESLOTS-1); 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)) ); */ m_last_dqs = dqs; m_last_rtt = ts->m_rtt; /* if (ts->m_used) { if (ts->m_read) assert((!dqs)&&(!m_last_dqs)); else assert((dqs) && (m_last_dqs)); } else if (!nxtts->m_used) assert(!dqs); */ // assert((!ts->m_used)||(ts->m_addr < (unsigned)m_memlen)); 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+ckODT-1)&(NTIMESLOTS-1)].m_rtt = (odt)&&(reset_n); ts->m_used = 0; ts->m_read = 0; ts->m_addr = -1; ts->m_rtt = 0; return (!busoe)?vl:data; }