gateware

A collection of little open source FPGA hobby projects
git clone http://frotz.net/git/gateware.git
Log | Files | Refs | README

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