sim-sdram.cpp (7798B)
1 // Copyright 2020, Brian Swetland <swetland@frotz.net> 2 // Licensed under the Apache License, Version 2.0. 3 4 #ifdef SDRAM 5 6 #include <stdint.h> 7 #include <string.h> 8 #include <stdio.h> 9 10 #include "sim-sdram.h" 11 12 // Geometry and Timing Configuration 13 // 14 #if 1 15 #define BANKBITS 1 16 #define COLBITS 8 17 #define ROWBITS 11 18 #else 19 #define BANKBITS 2 20 #define COLBITS 9 21 #define ROWBITS 13 22 #endif 23 24 #define tRCD 3 25 #define tRC 8 26 #define tRRD 3 27 #define tRP 3 28 #define tWR 2 29 #define tMRD 3 30 31 // Derived Parameters 32 // 33 #define ALLBITS (ROWBITS + BANKBITS + COLBITS) 34 #define ALLWORDS (1 << ALLBITS) 35 36 #define BANKS (1 << BANKBITS) 37 #define ROWS (1 << ROWBITS) 38 #define COLS (1 << COLBITS) 39 40 #define BANKMASK (BANKS - 1) 41 #define ROWMASK (ROWS - 1) 42 #define COLMASK (COLS - 1) 43 44 #define ADDR(bank, row, col) (\ 45 (((bank) & BANKMASK) << (ROWBITS + COLBITS)) |\ 46 (((row) & ROWMASK) << (COLBITS)) |\ 47 ((col) & COLMASK)) 48 49 #define CMD_SET_MODE 0b000 50 #define CMD_REFRESH 0b001 51 #define CMD_PRECHARGE 0b010 52 #define CMD_ACTIVE 0b011 53 #define CMD_WRITE 0b100 54 #define CMD_READ 0b101 55 #define CMD_STOP 0b110 56 #define CMD_NOP 0b111 57 58 #define BANK_UNKNOWN 0 59 #define BANK_IDLE 1 60 #define BANK_ACTIVE 2 61 #define BANK_READ 3 62 #define BANK_WRITE 4 63 #define BANK_PRECHARGE 5 64 #define BANK_REFRESH 6 65 #define BANK_OPENING 7 66 67 static const char* cname(unsigned n) { 68 switch (n) { 69 case CMD_SET_MODE: return "MODE"; 70 case CMD_REFRESH: return "REFR"; 71 case CMD_PRECHARGE: return "PCHG"; 72 case CMD_ACTIVE: return "ACTV"; 73 case CMD_WRITE: return "WRIT"; 74 case CMD_READ: return "READ"; 75 case CMD_STOP: return "STOP"; 76 case CMD_NOP: return "NOP"; 77 default: return "INVL"; 78 } 79 } 80 81 static const char* sname(unsigned n) { 82 switch (n) { 83 case BANK_UNKNOWN: return "UNKN"; 84 case BANK_IDLE: return "IDLE"; 85 case BANK_ACTIVE: return "ACTV"; 86 case BANK_READ: return "READ"; 87 case BANK_WRITE: return "WRIT"; 88 case BANK_PRECHARGE: return "PCHG"; 89 case BANK_REFRESH: return "REFR"; 90 case BANK_OPENING: return "OPEN"; 91 default: return "INVL"; 92 } 93 } 94 95 #define SN(n) sname(bank[n].state) 96 97 98 struct { 99 unsigned state; 100 unsigned rowaddr; 101 unsigned busy; // cycles until activated, precharged 102 } bank[BANKS]; 103 104 static struct { 105 unsigned pipe_data_o[tRCD]; // data out pipe 106 unsigned pipe_data_e[tRCD]; // data exists pipe 107 unsigned rd_burst; 108 unsigned wr_burst; 109 110 // activity 111 unsigned state; // BANK_READ or _WRITE 112 unsigned bankno; // bank number 113 unsigned count; // remaining cycles of that state 114 unsigned addr; 115 unsigned ap; // auto-precharge 116 } sdram; 117 118 // RD Bn Rnnnnn Cnnn -- Bn XXXXXXXX NNNN Bn XXXXXXXX NNNN 119 // 120 static uint16_t memory[ALLWORDS]; 121 122 void sim_dump(void) { 123 for (unsigned n = 0; n < BANKS; n++) { 124 printf("B%u %-4s %04x ", n, SN(n), bank[n].busy); 125 } 126 if (sdram.state != BANK_IDLE) { 127 printf("%s B%u R%05x C%03x (%u)\n", 128 sdram.state == BANK_WRITE ? "WR" : "RD", 129 sdram.bankno, bank[sdram.bankno].rowaddr, 130 sdram.addr & COLMASK, sdram.count); 131 } else { 132 printf("\n"); 133 } 134 } 135 136 void sim_sdram_init(void) { 137 memset(bank, 0, sizeof(bank)); 138 memset(&sdram, 0, sizeof(sdram)); 139 memset(memory, 0xFE, ALLWORDS * 2); 140 sdram.rd_burst = 1; 141 sdram.wr_burst = 1; 142 sdram.state = BANK_IDLE; 143 } 144 145 int sim_sdram(unsigned ctl, unsigned addr, unsigned din, unsigned* dout) { 146 unsigned a_bank = (addr >> ROWBITS) & BANKMASK; 147 unsigned a_row = addr & ROWMASK; 148 unsigned a_col = addr & COLMASK; 149 unsigned a_a10 = (addr >> 10) & 1; 150 151 printf("(%-4s) %06x %04x ", cname(ctl), addr, din); 152 for (unsigned n = 0; n < 3; n++) { 153 if (sdram.pipe_data_e[n]) { 154 printf("<%04x", sdram.pipe_data_o[n]); 155 } else { 156 printf("<----"); 157 } 158 } 159 printf("< "); 160 sim_dump(); 161 162 // drain output data pipe 163 if (sdram.pipe_data_e[0]) { 164 *dout = sdram.pipe_data_o[0]; 165 } else { 166 *dout = 0xE7E7; // DEBUG AID 167 } 168 sdram.pipe_data_e[0] = sdram.pipe_data_e[1]; 169 sdram.pipe_data_o[0] = sdram.pipe_data_o[1]; 170 sdram.pipe_data_e[1] = sdram.pipe_data_e[2]; 171 sdram.pipe_data_o[1] = sdram.pipe_data_o[2]; 172 sdram.pipe_data_e[2] = 0; 173 sdram.pipe_data_o[2] = 0; 174 175 // process bank timers 176 for (unsigned n = 0; n < BANKS; n++) { 177 if (bank[n].busy == 0) continue; 178 bank[n].busy--; 179 if (bank[n].busy != 0) continue; 180 switch (bank[n].state) { 181 case BANK_PRECHARGE: 182 bank[n].state = BANK_IDLE; 183 break; 184 case BANK_REFRESH: 185 bank[n].state = BANK_IDLE; 186 break; 187 case BANK_OPENING: 188 bank[n].state = BANK_ACTIVE; 189 break; 190 default: 191 printf("sdram: bank%d bad timer state %s\n", n, SN(n)); 192 return -1; 193 } 194 } 195 196 switch (ctl) { 197 case CMD_SET_MODE: 198 case CMD_REFRESH: 199 for (unsigned n = 0; n < BANKS; n++) { 200 if (bank[n].state != BANK_IDLE) { 201 printf("sdram: bank%d not idle for %s\n", n, 202 (ctl == CMD_SET_MODE) ? "SET_MODE" : "REFRESH"); 203 return -1; 204 } 205 } 206 // TODO 207 break; 208 case CMD_PRECHARGE: 209 for (unsigned n = 0; n < BANKS; n++) { 210 if (a_a10 || (a_bank == n)) { 211 if (bank[n].state == BANK_IDLE) { 212 continue; // NOP 213 } 214 if ((bank[n].state == BANK_READ) || 215 (bank[n].state == BANK_WRITE)) { 216 bank[n].state = BANK_ACTIVE; 217 sdram.state = BANK_IDLE; 218 continue; // cancel ongoing R/W 219 } 220 if ((bank[n].state == BANK_ACTIVE) || 221 (bank[n].state == BANK_UNKNOWN)) { 222 bank[n].state = BANK_PRECHARGE; 223 bank[n].busy = tRP - 1; 224 continue; 225 } 226 printf("sdram: bank%d cannot PRECHARGE from %s\n", n, SN(n)); 227 return -1; 228 } 229 } 230 break; 231 case CMD_ACTIVE: 232 if (bank[a_bank].state != BANK_IDLE) { 233 printf("sdram: bank%d cannot go ACTIVE from %s\n", a_bank, SN(a_bank)); 234 return -1; 235 } 236 bank[a_bank].state = BANK_OPENING; 237 bank[a_bank].busy = tRP - 1; 238 break; 239 case CMD_READ: 240 case CMD_WRITE: 241 if (bank[a_bank].state == BANK_WRITE) { 242 // truncate write, start new write burst 243 } else if (bank[a_bank].state == BANK_READ) { 244 // truncate read, start new write burst 245 } else if (bank[a_bank].state == BANK_ACTIVE) { 246 // cancel any r/w, start new 247 if (sdram.state != BANK_IDLE) { 248 unsigned n = sdram.bankno; 249 if (sdram.ap) { 250 bank[n].state = BANK_PRECHARGE; 251 bank[n].busy = tRP - 1; // ? tWR 252 } else { 253 bank[n].state = BANK_ACTIVE; 254 } 255 } 256 } else { 257 printf("sdram: bank%d cannot WRITE from state %s\n", a_bank, SN(a_bank)); 258 return -1; 259 } 260 if (ctl == CMD_WRITE) { 261 bank[a_bank].state = BANK_WRITE; 262 sdram.state = BANK_WRITE; 263 sdram.count = sdram.wr_burst; 264 } else { 265 bank[a_bank].state = BANK_READ; 266 sdram.state = BANK_READ; 267 sdram.count = sdram.rd_burst; 268 } 269 sdram.bankno = a_bank; 270 sdram.addr = ADDR(a_bank, bank[a_bank].rowaddr, a_col); 271 sdram.count = sdram.wr_burst; 272 sdram.ap = a_a10; // CHECK 273 break; 274 case CMD_STOP: 275 if ((sdram.state == BANK_READ) || (sdram.state == BANK_WRITE)) { 276 unsigned n = sdram.bankno; 277 sdram.state = BANK_IDLE; 278 if (sdram.ap) { 279 // TODO: double-check this is corrcet to honor 280 bank[n].state = BANK_PRECHARGE; 281 bank[n].busy = tRP - 1; 282 } else { 283 bank[n].state = BANK_ACTIVE; 284 } 285 } 286 break; 287 case CMD_NOP: 288 break; 289 } 290 291 // process active read or write operation 292 if (sdram.state != BANK_IDLE) { 293 if (sdram.state == BANK_WRITE) { 294 memory[sdram.addr] = din; 295 } else { 296 sdram.pipe_data_o[0] = memory[sdram.addr]; 297 sdram.pipe_data_e[0] = 1; 298 } 299 sdram.count--; 300 if (sdram.count == 0) { 301 sdram.state = BANK_IDLE; 302 if (sdram.ap) { 303 bank[sdram.bankno].state = BANK_PRECHARGE; 304 bank[sdram.bankno].busy = tRP - 1; // ? tWR 305 } else { 306 bank[sdram.bankno].state = BANK_ACTIVE; 307 } 308 } else { 309 sdram.addr = (sdram.addr & (~COLMASK)) | ((sdram.addr + 1) & COLMASK); 310 } 311 } 312 313 done: 314 return 0; 315 } 316 317 318 // TODO: DQM H = read: force dout to highz (2 cycles later) 319 // DQM H = write: mask write 320 // 321 // CKE / CS# - currently assumed always H and L 322 #endif