├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── COPYING ├── FTLs ├── bast_ftl.cpp ├── bdftl_ftl.cpp ├── dftl_ftl.cpp ├── dftl_parent.cpp ├── fast_ftl.cpp └── page_ftl.cpp ├── Makefile ├── README.FLASHSIM ├── SSDSim.cpp ├── SSDSim.h ├── cscope.sh ├── ctags.sh ├── readme.md ├── run_bimodal.cpp ├── run_correctness.cpp ├── run_debug.cpp ├── run_raid.cpp ├── run_test.cpp ├── run_test2.cpp ├── run_ufliptrace.cpp ├── ssd.conf ├── ssd.conf.testing ├── ssd.h ├── ssd_address.cpp ├── ssd_block.cpp ├── ssd_bm.cpp ├── ssd_bus.cpp ├── ssd_channel.cpp ├── ssd_config.cpp ├── ssd_controller.cpp ├── ssd_die.cpp ├── ssd_event.cpp ├── ssd_ftl.cpp ├── ssd_ftlparent.cpp ├── ssd_gc.cpp ├── ssd_package.cpp ├── ssd_page.cpp ├── ssd_plane.cpp ├── ssd_raidssd.cpp ├── ssd_ram.cpp ├── ssd_ssd.cpp ├── ssd_stats.cpp ├── ssd_wl.cpp ├── uml ├── uml.dia ├── uml.pdf └── verification.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | /Verification 2 | /Trace 3 | /RAID 4 | /Page 5 | /Correctness 6 | bimodal 7 | correctness 8 | debug 9 | dftl 10 | raid 11 | test 12 | test2 13 | ufliptrace 14 | *.o 15 | *.kdev4 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | sudo: false 3 | 4 | os: 5 | - linux 6 | - osx 7 | 8 | cache: 9 | apt: true 10 | 11 | addons: 12 | apt: 13 | packages: 14 | - libboost-all-dev 15 | 16 | install: 17 | travis_retry make -j 18 | 19 | script: 20 | - echo Testing correctness...; 21 | echo -en travis_fold:start:correctness; 22 | ./correctness; 23 | echo -en travis_fold:end:correctness 24 | - echo Testing raid...; 25 | echo -en travis_fold:start:raid; 26 | ./raid; 27 | echo -en travis_fold:end:raid 28 | - echo Testing test...; 29 | echo -en travis_fold:start:test; 30 | ./test; 31 | echo -en travis_fold:end:test 32 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | ### Changed 5 | - Debugger expects one more field in write request, indicating the data written. 6 | - Debugger responds to read request with data read along with physical address. 7 | -------------------------------------------------------------------------------- /FTLs/bast_ftl.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2011 Matias Bjørling */ 2 | 3 | /* bast_ftl.cpp */ 4 | 5 | /* FlashSim is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * any later version. */ 9 | 10 | /* FlashSim is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. */ 14 | 15 | /* You should have received a copy of the GNU General Public License 16 | * along with FlashSim. If not, see . */ 17 | 18 | /****************************************************************************/ 19 | 20 | /* Implementation of the BAST FTL described in the Paper 21 | * "A SPACE-EFFICIENT FLASH TRANSLATION LAYER FOR COMPACTFLASH SYSTEMS by Kim et. al." 22 | * 23 | * Notice: Startup procedures are not implemented as the drive is empty every time 24 | * the simulator is executed. i.e. OOB's is not filled with logical page address 25 | * at write and it is not read on startup to recreate mapping tables. 26 | * 27 | * Mapping table are implemented using simulation. A simulated read is performed 28 | * every time a page read is out a cache log page. A cache log page usually hold approx. 29 | * 1000 mappings. 30 | * 31 | * Second notice. Victim mappings still need to be implemented. 32 | */ 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include "../ssd.h" 41 | 42 | using namespace ssd; 43 | 44 | LogPageBlock::LogPageBlock() 45 | { 46 | pages = new int[BLOCK_SIZE]; 47 | aPages = new long[BLOCK_SIZE]; 48 | 49 | for (uint i=0;i> addressShift); 103 | Address eventAddress = Address(event.get_logical_address(), PAGE); 104 | 105 | LogPageBlock *logBlock = NULL; 106 | if (log_map.find(lookupBlock) != log_map.end()) 107 | logBlock = log_map[lookupBlock]; 108 | 109 | controller.stats.numMemoryRead++; 110 | 111 | // If page is in the log block 112 | if (logBlock != NULL && logBlock->pages[eventAddress.page] != -1) 113 | { 114 | Address returnAddress = Address(logBlock->address.get_linear_address()+logBlock->pages[eventAddress.page], PAGE); 115 | event.set_address(returnAddress); 116 | } 117 | else if ((data_list[lookupBlock] == -1 && logBlock != NULL && logBlock->pages[eventAddress.page] == -1) || (data_list[lookupBlock] == -1 && logBlock == NULL)) 118 | { 119 | event.set_address(Address(0, PAGE)); 120 | } else { // page is in the data block 121 | Address returnAddress = Address(data_list[lookupBlock]+ event.get_logical_address() % BLOCK_SIZE , PAGE); 122 | event.set_address(returnAddress); 123 | } 124 | 125 | if (controller.get_state(event.get_address()) == INVALID) 126 | event.set_address(Address(0, PAGE)); 127 | 128 | // Statistics 129 | controller.stats.numFTLRead++; 130 | 131 | return controller.issue(event); 132 | } 133 | 134 | enum status FtlImpl_Bast::write(Event &event) 135 | { 136 | LogPageBlock *logBlock = NULL; 137 | 138 | long lba = (event.get_logical_address() >> addressShift); 139 | 140 | Address eventAddress = Address(event.get_logical_address(), PAGE); 141 | 142 | if (log_map.find(lba) == log_map.end()) 143 | allocate_new_logblock(logBlock, lba, event); 144 | 145 | controller.stats.numMemoryRead++; 146 | 147 | logBlock = log_map[lba]; 148 | 149 | // Can it fit inside the existing log block. Issue the request. 150 | uint numValid = controller.get_num_valid(&logBlock->address); 151 | if (numValid < BLOCK_SIZE) 152 | { 153 | if (logBlock->pages[eventAddress.page] != -1) 154 | { 155 | Address replace_address = Address(logBlock->address.get_linear_address()+logBlock->pages[eventAddress.page], PAGE); 156 | event.set_replace_address(replace_address); 157 | } 158 | 159 | logBlock->pages[eventAddress.page] = numValid; 160 | 161 | Address logBlockAddress = logBlock->address; 162 | 163 | controller.get_free_page(logBlockAddress); 164 | event.set_address(logBlockAddress); 165 | } else { 166 | if (!is_sequential(logBlock, lba, event)) 167 | random_merge(logBlock, lba, event); 168 | 169 | allocate_new_logblock(logBlock, lba, event); 170 | logBlock = log_map[lba]; 171 | // Write the current io to a new block. 172 | logBlock->pages[eventAddress.page] = 0; 173 | Address dataPage = logBlock->address; 174 | dataPage.valid = PAGE; 175 | event.set_address(dataPage); 176 | 177 | } 178 | 179 | if (data_list[lba] != -1) 180 | { 181 | 182 | int offset = event.get_logical_address() % BLOCK_SIZE; 183 | Address replace = new Address(data_list[lba]+offset, PAGE); 184 | if (controller.get_block_pointer(replace)->get_state(offset) != EMPTY) 185 | event.set_replace_address(replace); 186 | } 187 | 188 | // Statistics 189 | controller.stats.numFTLWrite++; 190 | 191 | return controller.issue(event); 192 | } 193 | 194 | enum status FtlImpl_Bast::trim(Event &event) 195 | { 196 | // Find block 197 | long lookupBlock = (event.get_logical_address() >> addressShift); 198 | Address eventAddress = Address(event.get_logical_address(), PAGE); 199 | 200 | LogPageBlock *logBlock = NULL; 201 | if (log_map.find(lookupBlock) != log_map.end()) 202 | logBlock = log_map[lookupBlock]; 203 | 204 | controller.stats.numMemoryRead++; 205 | 206 | Address returnAddress; 207 | 208 | if (logBlock != NULL && logBlock->pages[eventAddress.page] != -1) // If page is in the log block 209 | { 210 | returnAddress = Address(logBlock->address.get_linear_address()+logBlock->pages[eventAddress.page], PAGE); 211 | Block *lBlock = controller.get_block_pointer(returnAddress); 212 | lBlock->invalidate_page(returnAddress.page); 213 | 214 | logBlock->pages[eventAddress.page] = -1; // Reset the mapping 215 | 216 | if (lBlock->get_state() == INACTIVE) // All pages invalid, force an erase. PTRIM style. 217 | { 218 | dispose_logblock(logBlock, lookupBlock); 219 | Block_manager::instance()->erase_and_invalidate(event, returnAddress, LOG); 220 | } 221 | 222 | } 223 | 224 | if (data_list[lookupBlock] != -1) // Datablock 225 | { 226 | Address dataAddress = Address(data_list[lookupBlock]+event.get_logical_address() % BLOCK_SIZE , PAGE); 227 | Block *dBlock = controller.get_block_pointer(dataAddress); 228 | dBlock->invalidate_page(dataAddress.page); 229 | 230 | if (dBlock->get_state() == INACTIVE) // All pages invalid, force an erase. PTRIM style. 231 | { 232 | data_list[lookupBlock] = -1; 233 | Block_manager::instance()->erase_and_invalidate(event, dataAddress, DATA); 234 | } 235 | 236 | } 237 | 238 | event.set_address(returnAddress); 239 | event.set_noop(true); 240 | 241 | // Statistics 242 | controller.stats.numFTLTrim++; 243 | 244 | return controller.issue(event); 245 | } 246 | 247 | 248 | void FtlImpl_Bast::allocate_new_logblock(LogPageBlock *logBlock, long lba, Event &event) 249 | { 250 | if (log_map.size() >= BAST_LOG_BLOCK_LIMIT) 251 | { 252 | int victim = random()%log_map.size()-1; 253 | std::map::iterator it = log_map.begin(); 254 | 255 | for (int i=0;iaddress = Block_manager::instance()->get_free_block(LOG, event); 269 | 270 | //printf("Using new log block with address: %lu Block: %u\n", logBlock->address.get_linear_address(), logBlock->address.block); 271 | log_map[lba] = logBlock; 272 | } 273 | 274 | void FtlImpl_Bast::dispose_logblock(LogPageBlock *logBlock, long lba) 275 | { 276 | log_map.erase(lba); 277 | delete logBlock; 278 | } 279 | 280 | bool FtlImpl_Bast::is_sequential(LogPageBlock* logBlock, long lba, Event &event) 281 | { 282 | // No page space. Merging required. 283 | /* 1. Log block merge 284 | * 2. Log block switch 285 | */ 286 | 287 | // Is block switch possible? i.e. log block switch 288 | bool isSequential = true; 289 | for (uint i=0;ipages[i] != (int)i) 290 | { 291 | isSequential = false; 292 | break; 293 | } 294 | 295 | if (isSequential) 296 | { 297 | Block_manager::instance()->promote_block(DATA); 298 | 299 | // Add to empty list i.e. switch without erasing the datablock. 300 | if (data_list[lba] != -1) 301 | { 302 | Address a = Address(data_list[lba], PAGE); 303 | Block_manager::instance()->erase_and_invalidate(event, a, DATA); 304 | } 305 | 306 | data_list[lba] = logBlock->address.get_linear_address(); 307 | dispose_logblock(logBlock, lba); 308 | 309 | controller.stats.numLogMergeSwitch++; 310 | update_map_block(event); 311 | } 312 | 313 | return isSequential; 314 | } 315 | 316 | bool FtlImpl_Bast::random_merge(LogPageBlock *logBlock, long lba, Event &event) 317 | { 318 | /* Do merge (n reads, n writes and 2 erases (gc'ed)) 319 | * 1. Write page to new data block 320 | * 1a Promote new log block. 321 | * 2. Create BLOCK_SIZE reads 322 | * 3. Create BLOCK_SIZE writes 323 | * 4. Invalidate data block 324 | * 5. promote new block as data block 325 | * 6. put data and log block into the invalidate list. 326 | */ 327 | 328 | Address eventAddress = Address(event.get_logical_address(), PAGE); 329 | Address newDataBlock = Block_manager::instance()->get_free_block(DATA, event); 330 | 331 | int t=0; 332 | for (uint i=0;ipages[i] != -1) 337 | readAddress.set_linear_address(logBlock->address.get_linear_address() + logBlock->pages[i], PAGE); 338 | else if (data_list[lba] != -1) 339 | readAddress.set_linear_address(data_list[lba] + i, PAGE); 340 | else 341 | continue; // Empty page 342 | 343 | if (controller.get_state(readAddress) == EMPTY) 344 | continue; 345 | 346 | if (controller.get_state(readAddress) == INVALID) // A page might be invalidated by trim 347 | continue; 348 | 349 | Event readEvent = Event(READ, event.get_logical_address(), 1, event.get_start_time()); 350 | readEvent.set_address(readAddress); 351 | controller.issue(readEvent); 352 | 353 | Event writeEvent = Event(WRITE, event.get_logical_address(), 1, event.get_start_time()+readEvent.get_time_taken()); 354 | writeEvent.set_address(Address(newDataBlock.get_linear_address() + i, PAGE)); 355 | writeEvent.set_payload((char*)page_data + readAddress.get_linear_address() * PAGE_SIZE); 356 | writeEvent.set_replace_address(readAddress); 357 | controller.issue(writeEvent); 358 | 359 | //event.consolidate_metaevent(writeEvent); 360 | event.incr_time_taken(writeEvent.get_time_taken() + readEvent.get_time_taken()); 361 | // Statistics 362 | controller.stats.numFTLRead++; 363 | controller.stats.numFTLWrite++; 364 | controller.stats.numWLRead++; 365 | controller.stats.numWLWrite++; 366 | t++; 367 | } 368 | 369 | // printf("t %i\n",t); 370 | 371 | // Invalidate inactive pages (LOG and DATA 372 | 373 | Block_manager::instance()->erase_and_invalidate(event, logBlock->address, LOG); 374 | 375 | if (data_list[lba] != -1) 376 | { 377 | Address a = Address(data_list[lba], PAGE); 378 | Block_manager::instance()->erase_and_invalidate(event, a, DATA); 379 | } 380 | 381 | // Update mapping 382 | data_list[lba] = newDataBlock.get_linear_address(); 383 | update_map_block(event); 384 | 385 | dispose_logblock(logBlock, lba); 386 | 387 | controller.stats.numLogMergeFull++; 388 | return true; 389 | } 390 | 391 | void FtlImpl_Bast::update_map_block(Event &event) 392 | { 393 | Event writeEvent = Event(WRITE, event.get_logical_address(), 1, event.get_start_time()); 394 | writeEvent.set_address(Address(0, PAGE)); 395 | writeEvent.set_noop(true); 396 | 397 | controller.issue(writeEvent); 398 | 399 | event.incr_time_taken(writeEvent.get_time_taken()); 400 | 401 | controller.stats.numGCWrite++; 402 | controller.stats.numFTLWrite++; 403 | } 404 | 405 | 406 | void FtlImpl_Bast::print_ftl_statistics() 407 | { 408 | Block_manager::instance()->print_statistics(); 409 | } 410 | 411 | -------------------------------------------------------------------------------- /FTLs/bdftl_ftl.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2011 Matias Bjørling */ 2 | 3 | /* bdftp_ftl.cpp */ 4 | 5 | /* FlashSim is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * any later version. */ 9 | 10 | /* FlashSim is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. */ 14 | 15 | /* You should have received a copy of the GNU General Public License 16 | * along with FlashSim. If not, see . */ 17 | 18 | /****************************************************************************/ 19 | 20 | /* Implementation of BDFTL. A block-level optimization for DFTL. 21 | * 22 | * Global Mapping Table GMT 23 | * Global Translation Directory GTD (Maintained in memory) 24 | * Cached Mapping Table CMT (Uses LRU to pick victim) 25 | * 26 | * Dlpn/Dppn Data Logical/Physical Page Number 27 | * Mlpn/Mppn Translation Logical/Physical Page Number 28 | */ 29 | 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include "../ssd.h" 39 | 40 | using namespace ssd; 41 | 42 | FtlImpl_BDftl::BPage::BPage() 43 | { 44 | this->pbn = -1; 45 | nextPage = 0; 46 | optimal = true; 47 | } 48 | 49 | FtlImpl_BDftl::FtlImpl_BDftl(Controller &controller): 50 | FtlImpl_DftlParent(controller) 51 | { 52 | block_map = new BPage[NUMBER_OF_ADDRESSABLE_BLOCKS]; 53 | trim_map = new bool[NUMBER_OF_ADDRESSABLE_BLOCKS*BLOCK_SIZE]; 54 | 55 | inuseBlock = NULL; 56 | 57 | printf("Using BDFTL.\n"); 58 | } 59 | 60 | 61 | 62 | FtlImpl_BDftl::~FtlImpl_BDftl(void) 63 | { 64 | delete[] block_map; 65 | delete[] trim_map; 66 | return; 67 | } 68 | 69 | enum status FtlImpl_BDftl::read(Event &event) 70 | { 71 | uint dlpn = event.get_logical_address(); 72 | uint dlbn = dlpn / BLOCK_SIZE; 73 | 74 | // Block-level lookup 75 | if (block_map[dlbn].optimal) 76 | { 77 | uint dppn = block_map[dlbn].pbn + (dlpn % BLOCK_SIZE); 78 | 79 | if (block_map[dlbn].pbn != (uint) -1) 80 | event.set_address(Address(dppn, PAGE)); 81 | else 82 | { 83 | event.set_address(Address(0, PAGE)); 84 | event.set_noop(true); 85 | } 86 | } else { // DFTL lookup 87 | resolve_mapping(event, false); 88 | 89 | MPage current = trans_map[dlpn]; 90 | 91 | if (current.ppn != -1) 92 | event.set_address(Address(current.ppn, PAGE)); 93 | else 94 | { 95 | event.set_address(Address(0, PAGE)); 96 | event.set_noop(true); 97 | } 98 | } 99 | 100 | event.incr_time_taken(RAM_READ_DELAY*2); 101 | controller.stats.numMemoryRead += 2; // Block-level lookup + range check 102 | controller.stats.numFTLRead++; // Page read 103 | 104 | return controller.issue(event); 105 | } 106 | 107 | 108 | enum status FtlImpl_BDftl::write(Event &event) 109 | { 110 | uint dlpn = event.get_logical_address(); 111 | uint dlbn = dlpn / BLOCK_SIZE; 112 | bool handled = false; 113 | 114 | // Update trim map 115 | trim_map[dlpn] = false; 116 | 117 | // Block-level lookup 118 | if (block_map[dlbn].optimal) 119 | { 120 | // Optimised case for block level lookup 121 | 122 | // Get new block if necessary 123 | if (block_map[dlbn].pbn == -1u && dlpn % BLOCK_SIZE == 0) 124 | block_map[dlbn].pbn = Block_manager::instance()->get_free_block(DATA, event).get_linear_address(); 125 | 126 | if (block_map[dlbn].pbn != -1u) 127 | { 128 | unsigned char dppn = dlpn % BLOCK_SIZE; 129 | if (block_map[dlbn].nextPage == dppn) 130 | { 131 | controller.stats.numMemoryWrite++; // Update next page 132 | event.incr_time_taken(RAM_WRITE_DELAY); 133 | event.set_address(Address(block_map[dlbn].pbn + dppn, PAGE)); 134 | block_map[dlbn].nextPage++; 135 | handled = true; 136 | } else { 137 | /* 138 | * Transfer the block to DFTL. 139 | * 1. Get number of pages to write 140 | * 2. Get start address for translation map 141 | * 3. Write mappings to trans_map 142 | * 4. Make block non-optimal 143 | * 5. Add the block to the block queue to be used later 144 | */ 145 | 146 | // 1-3 147 | uint numPages = block_map[dlbn].nextPage; 148 | long startAdr = dlbn * BLOCK_SIZE; 149 | 150 | Block *b = controller.get_block_pointer(Address(startAdr, PAGE)); 151 | 152 | for (uint i=0;iget_state(i) != INVALID); 155 | 156 | if (b->get_state(i) != VALID) 157 | continue; 158 | 159 | MPage current = trans_map[startAdr + i]; 160 | 161 | if (current.ppn != -1) 162 | { 163 | update_translation_map(current, block_map[dlbn].pbn+i); 164 | current.create_ts = event.get_start_time(); 165 | current.modified_ts = event.get_start_time(); 166 | current.cached = true; 167 | trans_map.replace(trans_map.begin()+startAdr+i, current); 168 | 169 | cmt++; 170 | 171 | event.incr_time_taken(RAM_WRITE_DELAY); 172 | controller.stats.numMemoryWrite++; 173 | } 174 | 175 | } 176 | 177 | // 4. Set block to non optimal 178 | event.incr_time_taken(RAM_WRITE_DELAY); 179 | controller.stats.numMemoryWrite++; 180 | block_map[dlbn].optimal = false; 181 | 182 | // 5. Add it to the queue to be used later. 183 | Block *block = controller.get_block_pointer(Address(block_map[dlbn].pbn, BLOCK)); 184 | if (block->get_pages_valid() != BLOCK_SIZE) 185 | { 186 | if (inuseBlock == NULL) 187 | inuseBlock = block; 188 | else 189 | blockQueue.push(block); 190 | } 191 | 192 | 193 | controller.stats.numPageBlockToPageConversion++; 194 | } 195 | } else { 196 | block_map[dlbn].optimal = false; 197 | } 198 | } 199 | 200 | if (!handled) 201 | { 202 | // Important order. As get_free_data_page might change current. 203 | long free_page = get_free_biftl_page(event); 204 | resolve_mapping(event, true); 205 | 206 | MPage current = trans_map[dlpn]; 207 | 208 | Address a = Address(current.ppn, PAGE); 209 | 210 | if (current.ppn != -1) 211 | event.set_replace_address(a); 212 | 213 | 214 | update_translation_map(current, free_page); 215 | trans_map.replace(trans_map.begin()+dlpn, current); 216 | 217 | // Finish DFTL logic 218 | event.set_address(Address(current.ppn, PAGE)); 219 | } 220 | 221 | controller.stats.numMemoryRead += 3; // Block-level lookup + range check + optimal check 222 | event.incr_time_taken(RAM_READ_DELAY*3); 223 | controller.stats.numFTLWrite++; // Page writes 224 | 225 | return controller.issue(event); 226 | } 227 | 228 | long FtlImpl_BDftl::get_free_biftl_page(Event &event) 229 | { 230 | // Important order. As get_free_data_page might change current. 231 | long free_page = -1; 232 | 233 | // Get next available data page 234 | if (inuseBlock == NULL) 235 | { 236 | // DFTL way 237 | free_page = get_free_data_page(event); 238 | } else { 239 | Address address; 240 | if (inuseBlock->get_next_page(address) == SUCCESS) 241 | { 242 | // Get page from biftl block space 243 | free_page = address.get_linear_address(); 244 | } 245 | else if (blockQueue.size() != 0) 246 | { 247 | inuseBlock = blockQueue.front(); 248 | blockQueue.pop(); 249 | if (inuseBlock->get_next_page(address) == SUCCESS) 250 | { 251 | // Get page from the next block in the biftl block space 252 | free_page = address.get_linear_address(); 253 | } 254 | else 255 | { 256 | assert(false); 257 | } 258 | } else { 259 | inuseBlock = NULL; 260 | // DFTL way 261 | free_page = get_free_data_page(event); 262 | } 263 | } 264 | 265 | assert(free_page != -1); 266 | 267 | return free_page; 268 | 269 | } 270 | 271 | enum status FtlImpl_BDftl::trim(Event &event) 272 | { 273 | uint dlpn = event.get_logical_address(); 274 | uint dlbn = dlpn / BLOCK_SIZE; 275 | 276 | // Update trim map 277 | trim_map[dlpn] = true; 278 | 279 | // Block-level lookup 280 | if (block_map[dlbn].optimal) 281 | { 282 | Address address = Address(block_map[dlbn].pbn+event.get_logical_address()%BLOCK_SIZE, PAGE); 283 | Block *block = controller.get_block_pointer(address); 284 | block->invalidate_page(address.page); 285 | 286 | if (block->get_state() == INACTIVE) // All pages invalid, force an erase. PTRIM style. 287 | { 288 | block_map[dlbn].pbn = -1; 289 | block_map[dlbn].nextPage = 0; 290 | Block_manager::instance()->erase_and_invalidate(event, address, DATA); 291 | } 292 | } else { // DFTL lookup 293 | 294 | MPage current = trans_map[dlpn]; 295 | if (current.ppn != -1) 296 | { 297 | Address address = Address(current.ppn, PAGE); 298 | Block *block = controller.get_block_pointer(address); 299 | block->invalidate_page(address.page); 300 | 301 | evict_specific_page_from_cache(event, dlpn); 302 | 303 | // Update translation map to default values. 304 | update_translation_map(current, -1); 305 | trans_map.replace(trans_map.begin()+dlpn, current); 306 | 307 | event.incr_time_taken(RAM_READ_DELAY); 308 | event.incr_time_taken(RAM_WRITE_DELAY); 309 | controller.stats.numMemoryRead++; 310 | controller.stats.numMemoryWrite++; 311 | } 312 | 313 | // Update trim map and update block map if all pages are trimmed. i.e. the state are reseted to optimal. 314 | long addressStart = dlpn - dlpn % BLOCK_SIZE; 315 | bool allTrimmed = true; 316 | for (uint i=addressStart;i invalidated_translation; 346 | /* 347 | * 1. Copy only valid pages in the victim block to the current data block 348 | * 2. Invalidate old pages 349 | * 3. mark their corresponding translation pages for update 350 | */ 351 | for (uint i=0;iget_state(i) != EMPTY); 354 | // When valid, two events are create, one for read and one for write. They are chained and the controller are 355 | // called to execute them. The execution time is then added to the real event. 356 | if (block->get_state(i) == VALID) 357 | { 358 | // Set up events. 359 | Event readEvent = Event(READ, event.get_logical_address(), 1, event.get_start_time()); 360 | readEvent.set_address(Address(block->get_physical_address()+i, PAGE)); 361 | 362 | // Execute read event 363 | if (controller.issue(readEvent) == FAILURE) 364 | printf("Data block copy failed."); 365 | 366 | // Get new address to write to and invalidate previous 367 | Event writeEvent = Event(WRITE, event.get_logical_address(), 1, event.get_start_time()+readEvent.get_time_taken()); 368 | Address dataBlockAddress = Address(get_free_data_page(event, false), PAGE); 369 | writeEvent.set_address(dataBlockAddress); 370 | writeEvent.set_replace_address(Address(block->get_physical_address()+i, PAGE)); 371 | 372 | // Setup the write event to read from the right place. 373 | writeEvent.set_payload((char*)page_data + (block->get_physical_address()+i) * PAGE_SIZE); 374 | 375 | if (controller.issue(writeEvent) == FAILURE) 376 | printf("Data block copy failed."); 377 | 378 | event.incr_time_taken(writeEvent.get_time_taken() + readEvent.get_time_taken()); 379 | 380 | // Update GTD 381 | long dataPpn = dataBlockAddress.get_linear_address(); 382 | 383 | // vpn -> Old ppn to new ppn 384 | //printf("%li Moving %li to %li\n", reverse_trans_map[block->get_physical_address()+i], block->get_physical_address()+i, dataPpn); 385 | invalidated_translation[reverse_trans_map[block->get_physical_address()+i]] = dataPpn; 386 | 387 | // Statistics 388 | controller.stats.numFTLRead++; 389 | controller.stats.numFTLWrite++; 390 | controller.stats.numWLRead++; 391 | controller.stats.numWLWrite++; 392 | controller.stats.numMemoryRead++; // Block->get_state(i) == VALID 393 | controller.stats.numMemoryWrite =+ 3; // GTD Update (2) + translation invalidate (1) 394 | } 395 | } 396 | 397 | /* 398 | * Perform batch update on the marked translation pages 399 | * 1. Update GDT and CMT if necessary. 400 | * 2. Simulate translation page updates. 401 | */ 402 | 403 | std::map dirtied_translation_pages; 404 | 405 | for (std::map::const_iterator i = invalidated_translation.begin(); i!=invalidated_translation.end(); ++i) 406 | { 407 | long real_vpn = (*i).first; 408 | long newppn = (*i).second; 409 | 410 | // Update translation map ( it also updates the CMT, as it is stored inside the GDT ) 411 | MPage current = trans_map[real_vpn]; 412 | 413 | update_translation_map(current, newppn); 414 | 415 | if (current.cached) 416 | current.modified_ts = event.get_start_time(); 417 | else 418 | { 419 | current.modified_ts = event.get_start_time(); 420 | current.create_ts = event.get_start_time(); 421 | current.cached = true; 422 | cmt++; 423 | } 424 | 425 | trans_map.replace(trans_map.begin()+real_vpn, current); 426 | } 427 | } 428 | 429 | // Returns true if the next page is in a new block 430 | bool FtlImpl_BDftl::block_next_new() 431 | { 432 | return (currentDataPage == -1 || currentDataPage % BLOCK_SIZE == BLOCK_SIZE -1); 433 | } 434 | 435 | void FtlImpl_BDftl::print_ftl_statistics() 436 | { 437 | printf("FTL Stats:\n"); 438 | printf(" Blocks total: %i\n", NUMBER_OF_ADDRESSABLE_BLOCKS); 439 | 440 | int numOptimal = 0; 441 | for (uint i=0;iprint_statistics(); 454 | } 455 | 456 | -------------------------------------------------------------------------------- /FTLs/dftl_ftl.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2011 Matias Bjørling */ 2 | 3 | /* dftp_ftl.cpp */ 4 | 5 | /* FlashSim is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * any later version. */ 9 | 10 | /* FlashSim is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. */ 14 | 15 | /* You should have received a copy of the GNU General Public License 16 | * along with FlashSim. If not, see . */ 17 | 18 | /****************************************************************************/ 19 | 20 | /* Implementation of the DFTL described in the paper 21 | * "DFTL: A Flasg Translation Layer Employing Demand-based Selective Caching og Page-level Address Mappings" 22 | * 23 | * Global Mapping Table GMT 24 | * Global Translation Directory GTD (Maintained in memory) 25 | * Cached Mapping Table CMT (Uses LRU to pick victim) 26 | * 27 | * Dlpn/Dppn Data Logical/Physical Page Number 28 | * Mlpn/Mppn Translation Logical/Physical Page Number 29 | */ 30 | 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include "../ssd.h" 40 | 41 | using namespace ssd; 42 | 43 | FtlImpl_Dftl::FtlImpl_Dftl(Controller &controller): 44 | FtlImpl_DftlParent(controller) 45 | { 46 | uint ssdSize = NUMBER_OF_ADDRESSABLE_BLOCKS * BLOCK_SIZE; 47 | printf("Total size to map: %uKB\n", ssdSize * PAGE_SIZE / 1024); 48 | printf("Using DFTL.\n"); 49 | return; 50 | } 51 | 52 | FtlImpl_Dftl::~FtlImpl_Dftl(void) 53 | { 54 | return; 55 | } 56 | 57 | enum status FtlImpl_Dftl::read(Event &event) 58 | { 59 | uint dlpn = event.get_logical_address(); 60 | 61 | resolve_mapping(event, false); 62 | MPage current = trans_map[dlpn]; 63 | if (current.ppn == -1) 64 | { 65 | event.set_address(Address(0, PAGE)); 66 | event.set_noop(true); 67 | } 68 | else 69 | event.set_address(Address(current.ppn, PAGE)); 70 | 71 | 72 | controller.stats.numFTLRead++; 73 | 74 | return controller.issue(event); 75 | } 76 | 77 | enum status FtlImpl_Dftl::write(Event &event) 78 | { 79 | uint dlpn = event.get_logical_address(); 80 | 81 | resolve_mapping(event, true); 82 | 83 | // Important order. As get_free_data_page might change current. 84 | long free_page = get_free_data_page(event); 85 | 86 | MPage current = trans_map[dlpn]; 87 | 88 | Address a = Address(current.ppn, PAGE); 89 | if (current.ppn != -1) 90 | event.set_replace_address(a); 91 | 92 | update_translation_map(current, free_page); 93 | trans_map.replace(trans_map.begin()+dlpn, current); 94 | 95 | Address b = Address(free_page, PAGE); 96 | event.set_address(b); 97 | 98 | controller.stats.numFTLWrite++; 99 | 100 | return controller.issue(event); 101 | } 102 | 103 | enum status FtlImpl_Dftl::trim(Event &event) 104 | { 105 | uint dlpn = event.get_logical_address(); 106 | 107 | event.set_address(Address(0, PAGE)); 108 | 109 | MPage current = trans_map[dlpn]; 110 | 111 | if (current.ppn != -1) 112 | { 113 | Address address = Address(current.ppn, PAGE); 114 | Block *block = controller.get_block_pointer(address); 115 | block->invalidate_page(address.page); 116 | 117 | evict_specific_page_from_cache(event, dlpn); 118 | 119 | update_translation_map(current, -1); 120 | 121 | trans_map.replace(trans_map.begin()+dlpn, current); 122 | } 123 | 124 | controller.stats.numFTLTrim++; 125 | 126 | return controller.issue(event); 127 | } 128 | 129 | void FtlImpl_Dftl::cleanup_block(Event &event, Block *block) 130 | { 131 | std::map invalidated_translation; 132 | /* 133 | * 1. Copy only valid pages in the victim block to the current data block 134 | * 2. Invalidate old pages 135 | * 3. mark their corresponding translation pages for update 136 | */ 137 | for (uint i=0;iget_state(i) != EMPTY); 140 | // When valid, two events are create, one for read and one for write. They are chained and the controller are 141 | // called to execute them. The execution time is then added to the real event. 142 | if (block->get_state(i) == VALID) 143 | { 144 | // Set up events. 145 | Event readEvent = Event(READ, event.get_logical_address(), 1, event.get_start_time()); 146 | readEvent.set_address(Address(block->get_physical_address()+i, PAGE)); 147 | 148 | // Execute read event 149 | if (controller.issue(readEvent) == FAILURE) 150 | printf("Data block copy failed."); 151 | 152 | // Get new address to write to and invalidate previous 153 | Event writeEvent = Event(WRITE, event.get_logical_address(), 1, event.get_start_time()+readEvent.get_time_taken()); 154 | Address dataBlockAddress = Address(get_free_data_page(event, false), PAGE); 155 | 156 | writeEvent.set_address(dataBlockAddress); 157 | 158 | writeEvent.set_replace_address(Address(block->get_physical_address()+i, PAGE)); 159 | 160 | // Setup the write event to read from the right place. 161 | writeEvent.set_payload((char*)page_data + (block->get_physical_address()+i) * PAGE_SIZE); 162 | 163 | if (controller.issue(writeEvent) == FAILURE) 164 | printf("Data block copy failed."); 165 | 166 | event.incr_time_taken(writeEvent.get_time_taken() + readEvent.get_time_taken()); 167 | 168 | // Update GTD 169 | long dataPpn = dataBlockAddress.get_linear_address(); 170 | 171 | // vpn -> Old ppn to new ppn 172 | //printf("%li Moving %li to %li\n", reverse_trans_map[block->get_physical_address()+i], block->get_physical_address()+i, dataPpn); 173 | invalidated_translation[reverse_trans_map[block->get_physical_address()+i]] = dataPpn; 174 | 175 | // Statistics 176 | controller.stats.numFTLRead++; 177 | controller.stats.numFTLWrite++; 178 | controller.stats.numWLRead++; 179 | controller.stats.numWLWrite++; 180 | controller.stats.numMemoryRead++; // Block->get_state(i) == VALID 181 | controller.stats.numMemoryWrite =+ 3; // GTD Update (2) + translation invalidate (1) 182 | } 183 | } 184 | 185 | /* 186 | * Perform batch update on the marked translation pages 187 | * 1. Update GDT and CMT if necessary. 188 | * 2. Simulate translation page updates. 189 | */ 190 | 191 | std::map dirtied_translation_pages; 192 | 193 | for (std::map::const_iterator i = invalidated_translation.begin(); i!=invalidated_translation.end(); ++i) 194 | { 195 | long real_vpn = (*i).first; 196 | long newppn = (*i).second; 197 | 198 | // Update translation map ( it also updates the CMT, as it is stored inside the GDT ) 199 | MPage current = trans_map[real_vpn]; 200 | 201 | update_translation_map(current, newppn); 202 | 203 | if (current.cached) 204 | current.modified_ts = event.get_start_time(); 205 | else 206 | { 207 | current.modified_ts = event.get_start_time(); 208 | current.create_ts = event.get_start_time(); 209 | current.cached = true; 210 | cmt++; 211 | } 212 | 213 | trans_map.replace(trans_map.begin()+real_vpn, current); 214 | } 215 | 216 | } 217 | 218 | void FtlImpl_Dftl::print_ftl_statistics() 219 | { 220 | Block_manager::instance()->print_statistics(); 221 | } 222 | -------------------------------------------------------------------------------- /FTLs/dftl_parent.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2011 Matias Bjørling */ 2 | 3 | /* dftp_ftl.cpp */ 4 | 5 | /* FlashSim is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * any later version. */ 9 | 10 | /* FlashSim is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. */ 14 | 15 | /* You should have received a copy of the GNU General Public License 16 | * along with FlashSim. If not, see . */ 17 | 18 | /****************************************************************************/ 19 | 20 | /* Implementation of the DFTL described in the paper 21 | * "DFTL: A Flasg Translation Layer Employing Demand-based Selective Caching og Page-level Address Mappings" 22 | * 23 | * Global Mapping Table GMT 24 | * Global Translation Directory GTD (Maintained in memory) 25 | * Cached Mapping Table CMT (Uses LRU to pick victim) 26 | * 27 | * Dlpn/Dppn Data Logical/Physical Page Number 28 | * Mlpn/Mppn Translation Logical/Physical Page Number 29 | */ 30 | 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include "../ssd.h" 41 | 42 | using namespace ssd; 43 | 44 | FtlImpl_DftlParent::MPage::MPage(long vpn) 45 | { 46 | this->vpn = vpn; 47 | this->ppn = -1; 48 | this->create_ts = -1; 49 | this->modified_ts = -1; 50 | this->last_visited_time = -1; 51 | this->cached = false; 52 | } 53 | 54 | double FtlImpl_DftlParent::mpage_last_visited_time_compare(const FtlImpl_DftlParent::MPage& mpage) 55 | { 56 | if (!mpage.cached) 57 | return std::numeric_limits::max(); 58 | 59 | return mpage.last_visited_time; 60 | } 61 | 62 | FtlImpl_DftlParent::FtlImpl_DftlParent(Controller &controller): 63 | FtlParent(controller) 64 | { 65 | addressPerPage = 0; 66 | cmt = 0; 67 | currentDataPage = -1; 68 | currentTranslationPage = -1; 69 | 70 | // Detect required number of bits for logical address size 71 | addressSize = log(NUMBER_OF_ADDRESSABLE_BLOCKS * BLOCK_SIZE)/log(2); 72 | 73 | // Find required number of bits for block size 74 | addressPerPage = (PAGE_SIZE/ceil(addressSize / 8.0)); // 8 bits per byte 75 | 76 | printf("Total required bits for representation: Address size: %i Total per page: %i \n", addressSize, addressPerPage); 77 | 78 | totalCMTentries = CACHE_DFTL_LIMIT * addressPerPage; 79 | printf("Number of elements in Cached Mapping Table (CMT): %i\n", totalCMTentries); 80 | 81 | // Initialise block mapping table. 82 | uint ssdSize = NUMBER_OF_ADDRESSABLE_BLOCKS * BLOCK_SIZE; 83 | 84 | trans_map.reserve(ssdSize); 85 | for (uint i=0;iinsert_events(event); 131 | 132 | if (currentDataPage == -1 || currentDataPage % BLOCK_SIZE == BLOCK_SIZE -1) 133 | currentDataPage = Block_manager::instance()->get_free_block(DATA, event).get_linear_address(); 134 | else 135 | currentDataPage++; 136 | 137 | return currentDataPage; 138 | } 139 | 140 | FtlImpl_DftlParent::~FtlImpl_DftlParent(void) 141 | { 142 | delete[] reverse_trans_map; 143 | } 144 | 145 | void FtlImpl_DftlParent::resolve_mapping(Event &event, bool isWrite) 146 | { 147 | uint dlpn = event.get_logical_address(); 148 | /* 1. Lookup in CMT if the mapping exist 149 | * 2. If, then serve 150 | * 3. If not, then goto GDT, lookup page 151 | * 4. If CMT full, evict a page 152 | * 5. Add mapping to CMT 153 | */ 154 | //printf("%i\n", cmt); 155 | if (lookup_CMT(event.get_logical_address(), event)) 156 | { 157 | controller.stats.numCacheHits++; 158 | 159 | MPage current = trans_map[dlpn]; 160 | if (isWrite) 161 | { 162 | current.modified_ts = event.get_start_time(); 163 | } 164 | current.last_visited_time = event.get_start_time(); 165 | trans_map.replace(trans_map.begin()+dlpn, current); 166 | 167 | // evict_page_from_cache(event); // no need to evict page from cache 168 | } else { 169 | controller.stats.numCacheFaults++; 170 | 171 | evict_page_from_cache(event); 172 | 173 | consult_GTD(dlpn, event); 174 | 175 | MPage current = trans_map[dlpn]; 176 | current.modified_ts = event.get_start_time(); 177 | current.last_visited_time = event.get_start_time(); 178 | if (isWrite) 179 | current.modified_ts++; 180 | current.create_ts = event.get_start_time(); 181 | current.cached = true; 182 | trans_map.replace(trans_map.begin()+dlpn, current); 183 | 184 | cmt++; 185 | } 186 | } 187 | 188 | void FtlImpl_DftlParent::evict_page_from_cache(Event &event) 189 | { 190 | while (cmt >= totalCMTentries) 191 | { 192 | // Find page to evict 193 | MpageByLastVisited::iterator evictit = boost::multi_index::get<1>(trans_map).begin(); 194 | MPage evictPage = *evictit; 195 | 196 | assert(evictPage.cached && evictPage.create_ts >= 0 && evictPage.modified_ts >= 0); 197 | 198 | if (evictPage.create_ts != evictPage.modified_ts) 199 | { 200 | // Evict page 201 | // Inform the ssd model that it should invalidate the previous page. 202 | // Calculate the start address of the translation page. 203 | int vpnBase = evictPage.vpn - evictPage.vpn % addressPerPage; 204 | 205 | for (int i=0;i= 0 && evictPage.modified_ts >= 0); 245 | 246 | if (evictPage.create_ts != evictPage.modified_ts) 247 | { 248 | // Evict page 249 | // Inform the ssd model that it should invalidate the previous page. 250 | // Calculate the start address of the translation page. 251 | int vpnBase = evictPage.vpn - evictPage.vpn % addressPerPage; 252 | 253 | for (int i=0;i. */ 17 | 18 | /****************************************************************************/ 19 | 20 | /* Implements a very simple page-level FTL without merge */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "../ssd.h" 27 | 28 | using namespace ssd; 29 | 30 | FtlImpl_Page::FtlImpl_Page(Controller &controller): 31 | FtlParent(controller) 32 | { 33 | trim_map = new bool[NUMBER_OF_ADDRESSABLE_BLOCKS * BLOCK_SIZE]; 34 | 35 | numPagesActive = 0; 36 | 37 | return; 38 | } 39 | 40 | FtlImpl_Page::~FtlImpl_Page(void) 41 | { 42 | 43 | return; 44 | } 45 | 46 | enum status FtlImpl_Page::read(Event &event) 47 | { 48 | event.set_address(Address(0, PAGE)); 49 | event.set_noop(true); 50 | 51 | controller.stats.numFTLRead++; 52 | 53 | return controller.issue(event); 54 | } 55 | 56 | enum status FtlImpl_Page::write(Event &event) 57 | { 58 | event.set_address(Address(1, PAGE)); 59 | event.set_noop(true); 60 | 61 | controller.stats.numFTLWrite++; 62 | 63 | if (numPagesActive == NUMBER_OF_ADDRESSABLE_BLOCKS * BLOCK_SIZE) 64 | { 65 | numPagesActive -= BLOCK_SIZE; 66 | 67 | Event eraseEvent = Event(ERASE, event.get_logical_address(), 1, event.get_start_time()); 68 | eraseEvent.set_address(Address(0, PAGE)); 69 | 70 | if (controller.issue(eraseEvent) == FAILURE) printf("Erase failed"); 71 | 72 | event.incr_time_taken(eraseEvent.get_time_taken()); 73 | 74 | controller.stats.numFTLErase++; 75 | } 76 | 77 | numPagesActive++; 78 | 79 | 80 | return controller.issue(event); 81 | } 82 | 83 | enum status FtlImpl_Page::trim(Event &event) 84 | { 85 | controller.stats.numFTLTrim++; 86 | 87 | uint dlpn = event.get_logical_address(); 88 | 89 | if (!trim_map[event.get_logical_address()]) 90 | trim_map[event.get_logical_address()] = true; 91 | 92 | // Update trim map and update block map if all pages are trimmed. i.e. the state are reseted to optimal. 93 | long addressStart = dlpn - dlpn % BLOCK_SIZE; 94 | bool allTrimmed = true; 95 | for (uint i=addressStart;iatgmaildotcom 3 | 4 | README is part of FlashSim. 5 | 6 | FlashSim is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | any later version. 10 | 11 | FlashSim is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with FlashSim. If not, see . 18 | 19 | ############################################################################## 20 | 21 | README 22 | 23 | To use FlashSim with a main file, use the "ssd" (default) make target 24 | and link the object files with your code that contains a main function. Some 25 | examples can be found in the "test" and "trace" make targets and corresponding 26 | source files. 27 | 28 | Users must provide their FTL scheme to run FlashSim, which should include a 29 | FTL, wear-leveler, and garbage-collector class. Please note that FLASHSIM WILL 30 | NOT WORK WITHOUT PROVIDING A FTL SCHEME. The UML diagram has been provided to 31 | assist with FTL development. Many private functions of the Controller class 32 | made available to the FTL class will assist users in FTL development. Users can 33 | run the "test" and "trace" make target output binaries to perform basic 34 | testing of their FTL schemes. 35 | 36 | Before running the FlashSim module, users should update the configuration file 37 | "ssd.conf" according to their specificiations. Descriptions of configuration 38 | settings can be found in the sample configuration file. 39 | 40 | FlashSim was designed to be capable of being modularly integrated with Disksim. 41 | The Ssd::event_arrive() function signature in ssd_ssd.cpp was designed to match 42 | the event_arrive() function signature that Disksim uses to send events to disks. 43 | 44 | Any questions, comments, suggestions, or code additions are welcome. 45 | -------------------------------------------------------------------------------- /SSDSim.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * SSDSim.cpp 3 | * 4 | * Created on: Feb 21, 2011 5 | * Author: silverwolf 6 | */ 7 | 8 | #include "SSDSim.h" 9 | #include 10 | 11 | #ifdef __cplusplus 12 | #include "ssd.h" 13 | #include 14 | using namespace ssd; 15 | #endif 16 | 17 | void SSD_Initialize() 18 | { 19 | load_config(); 20 | print_config(NULL); 21 | 22 | ssdImpl = new Ssd(); 23 | 24 | gettimeofday(&ssd_boot_time, NULL); 25 | 26 | printf("Booted the SSD Simulator.\n"); 27 | } 28 | 29 | void SSD_Cleanup() 30 | { 31 | printf("SSD Simulator killed.\n"); 32 | delete ssdImpl; 33 | } 34 | 35 | void SSD_Write(unsigned long long address, int size, void *buf) 36 | { 37 | gettimeofday(&ssd_request_time, NULL); 38 | 39 | double time = ((ssd_request_time.tv_sec - ssd_boot_time.tv_sec) * 1000 + (ssd_request_time.tv_usec - ssd_boot_time.tv_usec) / 1000.0) + 0.5; 40 | 41 | for (int i=0;ievent_arrive(WRITE, address, 1, time, NULL); 44 | printf("Write time address %llu (%i): %.20lf at %.3f\n", address, size, result, time); 45 | } 46 | } 47 | 48 | void SSD_Read(unsigned long long address, int size, void *buf) 49 | { 50 | gettimeofday(&ssd_request_time, NULL); 51 | 52 | double time = ((ssd_request_time.tv_sec - ssd_boot_time.tv_sec) * 1000 + (ssd_request_time.tv_usec - ssd_boot_time.tv_usec) / 1000.0) + 0.5; 53 | 54 | for (int i=0;ievent_arrive(READ, address, 1, time, NULL); 57 | printf("Read time %llu (%i): %.20lf at %.3f\n", address, size, result, time); 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /SSDSim.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SSDSim.h 3 | * 4 | * Created on: Feb 21, 2011 5 | * Author: Matias Bjørling 6 | */ 7 | 8 | #ifndef SSDSIM_H_ 9 | #define SSDSIM_H_ 10 | 11 | 12 | #ifdef __cplusplus 13 | #include "ssd.h" 14 | 15 | using namespace ssd; 16 | 17 | Ssd *ssdImpl; 18 | struct timeval ssd_boot_time, ssd_request_time; 19 | 20 | extern "C" { 21 | #endif 22 | 23 | void SSD_Initialize(); 24 | void SSD_Cleanup(); 25 | void SSD_Write(unsigned long long address, int size, void *buf); 26 | void SSD_Read(unsigned long long address, int size, void *buf); 27 | 28 | #ifdef __cplusplus 29 | } // extern "C" 30 | #endif 31 | 32 | #endif /* SSDSIM_H_ */ 33 | -------------------------------------------------------------------------------- /cscope.sh: -------------------------------------------------------------------------------- 1 | # Copyright 2009, 2010 Brendan Tauras 2 | 3 | # cscope.sh is part of FlashSim. 4 | 5 | # FlashSim is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # any later version. 9 | 10 | # FlashSim is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | 15 | # You should have received a copy of the GNU General Public License 16 | # along with FlashSim. If not, see . 17 | 18 | ############################################################################## 19 | 20 | #!/bin/env sh 21 | 22 | export VIEWER=vim 23 | `make files | tail -1` | cscope 24 | cscope 25 | -------------------------------------------------------------------------------- /ctags.sh: -------------------------------------------------------------------------------- 1 | # Copyright 2009, 2010 Brendan Tauras 2 | 3 | # ctags.sh is part of FlashSim. 4 | 5 | # FlashSim is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # any later version. 9 | 10 | # FlashSim is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | 15 | # You should have received a copy of the GNU General Public License 16 | # along with FlashSim. If not, see . 17 | 18 | ############################################################################## 19 | 20 | #!/bin/env sh 21 | ctags --extra=+fq --fields=+afikKlmnsSzt -h cpp.h *.cpp *.h 22 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Extended FlashSim 2 | [![Build Status](https://travis-ci.org/MatiasBjorling/flashsim.svg?branch=master)](https://travis-ci.org/MatiasBjorling/flashsim) 3 | 4 | This project extends FlashSim with BAST, FAST and DFTL FTLs. 5 | 6 | Please reference if you use for your research. 7 | 8 | Bibtex 9 | 10 | @article{extendedflashsim, 11 | Author = {Matias Bj{\o}rling}, 12 | Title = {Extended FlashSim}, 13 | Url = {https://github.com/MatiasBjorling/flashsim}, 14 | Year = 2011} 15 | 16 | The FTL algorihms are implemented on top of the Flash based SSD simulator implementation created by Brendan Tauras btauras, Youngjae Kim, Aayush Gupta at Pennsylvania State University. 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /run_bimodal.cpp: -------------------------------------------------------------------------------- 1 | /* FlashSim is free software: you can redistribute it and/or modify 2 | * it under the terms of the GNU General Public License as published by 3 | * the Free Software Foundation, either version 3 of the License, or 4 | * any later version. */ 5 | 6 | /* FlashSim is distributed in the hope that it will be useful, 7 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU General Public License for more details. */ 10 | 11 | /* You should have received a copy of the GNU General Public License 12 | * along with FlashSim. If not, see . */ 13 | 14 | /****************************************************************************/ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "ssd.h" 26 | 27 | using namespace ssd; 28 | 29 | /** 30 | * Benchmark plan 31 | * 1. The number of writes should be the size of the device simulated. 32 | * 2. Trim a designated area. E.g. 256MB, measure the time it takes to perform the trim with the implemented FTLs. 33 | * 3. Measure the time required to overwrite the area and compare with the other FTLs. 34 | * 4. Create a report with shows the differences in response time using CDF's. 35 | * 36 | * Test assumes a 6GB SSD, with Block-size 64 and Page size 2048 bytes. 37 | */ 38 | 39 | int main(int argc, char **argv){ 40 | 41 | long vaddr; 42 | 43 | double arrive_time; 44 | 45 | load_config(); 46 | print_config(NULL); 47 | 48 | Ssd ssd; 49 | 50 | printf("INITIALIZING SSD Bimodal\n"); 51 | 52 | srandom(1); 53 | int preIO = SSD_SIZE * PACKAGE_SIZE * DIE_SIZE * PLANE_SIZE * BLOCK_SIZE; 54 | 55 | if (FTL_IMPLEMENTATION == 0) // PAGE 56 | preIO -= 16*BLOCK_SIZE; 57 | 58 | if (FTL_IMPLEMENTATION == 1) // BAST 59 | preIO -= (BAST_LOG_BLOCK_LIMIT*BLOCK_SIZE)*2; 60 | 61 | if (FTL_IMPLEMENTATION == 2) // FAST 62 | preIO -= (FAST_LOG_BLOCK_LIMIT*BLOCK_SIZE)*1.1; 63 | 64 | if (FTL_IMPLEMENTATION > 2) // DFTL BIFTL 65 | preIO -= 512; 66 | 67 | int deviceSize = 3145216; 68 | 69 | if (preIO > deviceSize) 70 | preIO = deviceSize; 71 | 72 | printf("Writes %i pages for startup out of %i total pages.\n", preIO, SSD_SIZE * PACKAGE_SIZE * DIE_SIZE * PLANE_SIZE * BLOCK_SIZE); 73 | 74 | double start_time = 0; 75 | double timeMultiplier = 10000; 76 | 77 | double read_time = 0; 78 | double write_time = 0; 79 | double trim_time = 0; 80 | 81 | unsigned long num_reads = 0; 82 | unsigned long num_writes = 0; 83 | unsigned long num_trims = 0; 84 | 85 | std::vector avgsTrim; 86 | std::vector avgsWrite1; 87 | std::vector avgsRead1; 88 | std::vector avgsRead2; 89 | std::vector avgsRead3; 90 | std::vector avgsTrim2; 91 | std::vector avgsWrite2; 92 | std::vector avgsRead4; 93 | std::vector avgsWrite3; 94 | 95 | 96 | avgsTrim.reserve(1024*64); 97 | avgsWrite1.reserve(1024*64); 98 | avgsRead1.reserve(1024*64); 99 | avgsRead2.reserve(1024*64); 100 | avgsRead3.reserve(1024*64); 101 | avgsTrim2.reserve(1024*64); 102 | avgsWrite2.reserve(1024*64); 103 | avgsRead4.reserve(1024*64); 104 | avgsWrite3.reserve(1024*64); 105 | 106 | // Reset statistics 107 | ssd.reset_statistics(); 108 | 109 | // 1. Write random to the size of the device 110 | srand(1); 111 | double afterFormatStartTime = 0; 112 | //for (int i=0; i 400) 215 | printf("Trim: %i %f\n", i, trim_time); 216 | 217 | } 218 | 219 | for (int i=startTrim; i 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | using namespace ssd; 18 | 19 | int open_temp_file(unsigned int file_size = 10 * 1024 * 1024) 20 | { 21 | std::string out_path = "/tmp/garbage.XXXXXX"; 22 | int fd = mkstemp(&*out_path.begin()); 23 | if (fd != -1) 24 | { 25 | ftruncate(fd, file_size); 26 | printf("Created temporary file %s of size %u\n", out_path.c_str(), file_size); 27 | } 28 | else 29 | { 30 | printf("Failed to create temp file\n"); 31 | } 32 | return fd; 33 | } 34 | 35 | double timings = 0.0; 36 | 37 | double do_seq(Ssd *ssd, event_type type, void *test, unsigned int file_size) 38 | { 39 | unsigned int adr, i = 0; 40 | double result = 0; 41 | for (adr = 0; adr < file_size;adr += PAGE_SIZE) 42 | { 43 | double iotime = ssd->event_arrive(type, i, 1, timings, (char*)test + adr); 44 | //printf("IO Execution time: %f\n", iotime); 45 | result += iotime; 46 | timings += iotime; 47 | if (type == READ) 48 | { 49 | if (ssd->get_result_buffer() == NULL) 50 | printf("Data has not been written\n"); 51 | else if (memcmp(ssd->get_result_buffer(), (char*)test + adr, PAGE_SIZE) != 0) 52 | fprintf(stderr, "i: %i ", i); 53 | } 54 | i++; 55 | } 56 | fprintf(stderr,"\n"); 57 | return result; 58 | } 59 | 60 | double do_seq_backward(Ssd *ssd, event_type type, void *test, unsigned int file_size) 61 | { 62 | unsigned int adr, i = BLOCK_SIZE-1, j=0; 63 | double result = 0; 64 | for (adr = file_size; adr > 0;adr -= PAGE_SIZE) 65 | { 66 | double iotime = ssd->event_arrive(type, j+i, 1, timings, (char*)test + adr - PAGE_SIZE); 67 | 68 | if (type == READ && memcmp(ssd->get_result_buffer(), (char*)test + adr - PAGE_SIZE, PAGE_SIZE) != 0) 69 | fprintf(stderr, "Err. Data does not compare. i: %i\n", j+i); 70 | 71 | result += iotime; 72 | timings += iotime; 73 | 74 | i--; 75 | 76 | if (i == -1u) 77 | { 78 | i = BLOCK_SIZE - 1; 79 | j += BLOCK_SIZE; 80 | } 81 | } 82 | return result; 83 | } 84 | 85 | double do_random(Ssd *ssd, event_type type, void *test, unsigned int file_size) 86 | { 87 | unsigned int adr, i = 0; 88 | double result = 0; 89 | for (adr = 0; adr < file_size;adr += PAGE_SIZE) 90 | { 91 | result += ssd->event_arrive(type, i, 1, (double) adr, (char*)test + adr); 92 | if (type == READ) 93 | { 94 | if (memcmp(ssd->get_result_buffer(), (char*)test + adr, PAGE_SIZE) != 0) 95 | fprintf(stderr, "Err. Data does not compare. i: %i\n", i); 96 | } 97 | i++; 98 | } 99 | return result; 100 | } 101 | 102 | int main(int argc, char** argv) 103 | { 104 | load_config(); 105 | print_config(NULL); 106 | printf("\n"); 107 | 108 | Ssd *ssd = new Ssd(); 109 | 110 | // create memory mapping of file that we are going to check with 111 | int fd; 112 | if (argc == 1) 113 | fd = open_temp_file(); 114 | else 115 | fd = open(argv[1], O_RDONLY); 116 | struct stat st; 117 | fstat(fd, &st); 118 | 119 | void *test_data = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 120 | 121 | if (test_data == MAP_FAILED) 122 | fprintf(stderr, "File not mapped."); 123 | 124 | printf("Size of testfile: %iKB\n", (int)st.st_size/1024); 125 | 126 | /* 127 | * Experiment setup 128 | * 1. Do linear write and read. 129 | * 2. Write linear again and read. 130 | * 2. Do semi-random linear 131 | * 3. Do random 132 | * 4. Do backward linear 133 | */ 134 | 135 | double result = 0; 136 | 137 | printf("Test 1. Write sequential test data.\n"); 138 | result += do_seq(ssd, WRITE, test_data, st.st_size); 139 | 140 | printf("Test 1. Write sequential test data.\n"); 141 | result += do_seq(ssd, WRITE, test_data, st.st_size); 142 | 143 | printf("Test 1. Write sequential test data.\n"); 144 | result += do_seq(ssd, WRITE, test_data, st.st_size); 145 | // 146 | // printf("Test 1. Trim data.\n"); 147 | // result += do_seq(ssd, TRIM, test_data, st.st_size); 148 | 149 | printf("Test 1. Write sequential test data.\n"); 150 | result += do_seq(ssd, WRITE, test_data, st.st_size); 151 | 152 | printf("Test 2. Read sequential test data.\n"); 153 | result += do_seq(ssd, READ, test_data, st.st_size); 154 | 155 | // printf("Test 6. Write backward sequential test data.\n"); 156 | // result += do_seq_backward(ssd, WRITE, test_data, st.st_size); 157 | // 158 | // printf("Test 9. Read backward sequential test data.\n"); 159 | // result += do_seq_backward(ssd, READ, test_data, st.st_size); 160 | 161 | // printf("Test 3. Write second write.\n"); 162 | // result += do_seq(ssd, WRITE, test_data, st.st_size); 163 | // 164 | // printf("Test 4. Write third write.\n"); 165 | // result += do_seq_backward(ssd, WRITE, test_data, st.st_size); 166 | // 167 | // printf("Test 5. Read sequential test data.\n"); 168 | // result += do_seq_backward(ssd, READ, test_data, st.st_size); 169 | // 170 | // printf("Test 6. Write backward sequential test data.\n"); 171 | // result += do_seq_backward(ssd, WRITE, test_data, st.st_size); 172 | // 173 | // printf("Test 7. Read backward sequential test data.\n"); 174 | // result += do_seq_backward(ssd, READ, test_data, st.st_size); 175 | // 176 | // printf("Test 8. Write backward sequential test data again.\n"); 177 | // result += do_seq_backward(ssd, WRITE, test_data, st.st_size); 178 | // 179 | // printf("Test 9. Read backward sequential test data.\n"); 180 | // result += do_seq_backward(ssd, READ, test_data, st.st_size); 181 | 182 | printf("Write time: %.10lfs\n", result); 183 | 184 | ssd->print_statistics(); 185 | delete ssd; 186 | return 0; 187 | } 188 | -------------------------------------------------------------------------------- /run_debug.cpp: -------------------------------------------------------------------------------- 1 | /* FlashSim is free software: you can redistribute it and/or modify 2 | * it under the terms of the GNU General Public License as published by 3 | * the Free Software Foundation, either version 3 of the License, or 4 | * any later version. */ 5 | 6 | /* FlashSim is distributed in the hope that it will be useful, 7 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU General Public License for more details. */ 10 | 11 | /* You should have received a copy of the GNU General Public License 12 | * along with FlashSim. If not, see . */ 13 | 14 | /****************************************************************************/ 15 | 16 | /* Interactive debugger. 17 | * Yishuai Li 18 | * Dec 29, 2015 */ 19 | 20 | #include 21 | #include 22 | #include "ssd.h" 23 | 24 | using namespace ssd; 25 | 26 | /* Input Format 27 | 28 | Each request consists of two or three fields, separated by whitespaces: 29 | 1. One character, representing the type of I/O: (R)ead, (W)rite, (T)rim; 30 | 2. One integer, representing the virtual address; 31 | 3. (Write only) One integer, representing the data written. 32 | 33 | * Output Format 34 | 35 | For read requests, the program responds with two integers: the data read, and 36 | the physical address being read. 37 | */ 38 | 39 | void debug(Ssd& ssd) throw (std::invalid_argument) 40 | { 41 | char ioType; 42 | ulong vaddr; 43 | char buffer[PAGE_SIZE]; 44 | while (std::cin >> ioType >> vaddr) 45 | { 46 | event_type type; 47 | switch (ioType) 48 | { 49 | case 'R': 50 | case 'r': 51 | type = READ; 52 | break; 53 | case 'W': 54 | case 'w': 55 | type = WRITE; 56 | std::cin >> *(int*) buffer; 57 | break; 58 | case 'T': 59 | case 't': 60 | type = TRIM; 61 | break; 62 | default: 63 | throw std::invalid_argument("Invalid I/O type!"); 64 | } 65 | global_buffer = nullptr; 66 | ssd.event_arrive(type, vaddr, 1, time(NULL), buffer); 67 | if (type == READ) 68 | { 69 | std::cout << (global_buffer ? *(int*) global_buffer : 0) << '\t'; 70 | std::cout << global_buffer << std::endl; 71 | } 72 | } 73 | } 74 | 75 | int main() throw (std::invalid_argument) 76 | { 77 | load_config(); 78 | print_config(stderr); 79 | 80 | Ssd ssd; 81 | 82 | std::clog << "INITIALIZING SSD" << std::endl; 83 | 84 | debug(ssd); 85 | } 86 | 87 | -------------------------------------------------------------------------------- /run_raid.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2012 Matias Bjørling */ 2 | 3 | /* FlashSim is free software: you can redistribute it and/or modify 4 | * it under the terms of the GNU General Public License as published by 5 | * the Free Software Foundation, either version 3 of the License, or 6 | * any later version. */ 7 | 8 | /* FlashSim is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. */ 12 | 13 | /* You should have received a copy of the GNU General Public License 14 | * along with FlashSim. If not, see . */ 15 | 16 | /****************************************************************************/ 17 | 18 | #include "ssd.h" 19 | 20 | #define SIZE 10 21 | 22 | using namespace ssd; 23 | 24 | int main() 25 | { 26 | load_config(); 27 | print_config(NULL); 28 | 29 | RaidSsd *ssd = new RaidSsd(); 30 | 31 | double result; 32 | double cur_time = 1; 33 | 34 | for (int i = 0; i < SIZE; i++) 35 | { 36 | result = ssd -> event_arrive(WRITE, i*2, 1, 0); 37 | cur_time += result; 38 | } 39 | for (int i = 0; i < SIZE; i++) 40 | { 41 | result = ssd -> event_arrive(READ, i*2, 1, 0); 42 | cur_time += result; 43 | } 44 | 45 | printf("Total execution time %f\n", cur_time); 46 | 47 | delete ssd; 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /run_test.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2009, 2010 Brendan Tauras */ 2 | 3 | /* run_test.cpp is part of FlashSim. */ 4 | 5 | /* FlashSim is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * any later version. */ 9 | 10 | /* FlashSim is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. */ 14 | 15 | /* You should have received a copy of the GNU General Public License 16 | * along with FlashSim. If not, see . */ 17 | 18 | /****************************************************************************/ 19 | 20 | /* Basic test driver 21 | * Brendan Tauras 2009-11-02 22 | * 23 | * driver to create and run a very basic test of writes then reads */ 24 | 25 | #include "ssd.h" 26 | 27 | #define SIZE 130 28 | 29 | using namespace ssd; 30 | 31 | int main() 32 | { 33 | load_config(); 34 | print_config(NULL); 35 | //printf("Press ENTER to continue..."); 36 | //getchar(); 37 | printf("\n"); 38 | 39 | Ssd *ssd = new Ssd(); 40 | 41 | double result; 42 | 43 | // // Test one write to some blocks. 44 | // for (int i = 0; i < SIZE; i++) 45 | // { 46 | // /* event_arrive(event_type, logical_address, size, start_time) */ 47 | // result = ssd -> event_arrive(WRITE, i*100000, 1, (double) 1+(250*i)); 48 | // 49 | // printf("Write time: %.20lf\n", result); 50 | //// result = ssd -> event_arrive(WRITE, i+10240, 1, (double) 1); 51 | //// 52 | // } 53 | // for (int i = 0; i < SIZE; i++) 54 | // { 55 | // /* event_arrive(event_type, logical_address, size, start_time) */ 56 | // result = ssd -> event_arrive(READ, i*100000, 1, (double) 1+(500*i)); 57 | // printf("Read time : %.20lf\n", result); 58 | //// result = ssd -> event_arrive(READ, i, 1, (double) 1); 59 | //// printf("Read time : %.20lf\n", result); 60 | // } 61 | 62 | // // Test writes and read to same block. 63 | // for (int i = 0; i < SIZE; i++) 64 | // { 65 | // result = ssd -> event_arrive(WRITE, i%64, 1, (double) 1+(250*i)); 66 | // 67 | // printf("Write time: %.20lf\n", result); 68 | // } 69 | // for (int i = 0; i < SIZE; i++) 70 | // { 71 | // result = ssd -> event_arrive(READ, i%64, 1, (double) 1+(500*i)); 72 | // printf("Read time : %.20lf\n", result); 73 | // } 74 | 75 | // Test random writes to a block 76 | result = ssd -> event_arrive(WRITE, 5, 1, (double) 0.0); 77 | printf("Write time: %.20lf\n", result); 78 | result = ssd -> event_arrive(WRITE, 4, 1, (double) 300.0); 79 | printf("Write time: %.20lf\n", result); 80 | result = ssd -> event_arrive(WRITE, 3, 1, (double) 600.0); 81 | printf("Write time: %.20lf\n", result); 82 | result = ssd -> event_arrive(WRITE, 2, 1, (double) 900.0); 83 | printf("Write time: %.20lf\n", result); 84 | result = ssd -> event_arrive(WRITE, 1, 1, (double) 1200.0); 85 | printf("Write time: %.20lf\n", result); 86 | result = ssd -> event_arrive(WRITE, 0, 1, (double) 1500.0); 87 | printf("Write time: %.20lf\n", result); 88 | 89 | for (int i = 0; i < SIZE-6; i++) 90 | { 91 | /* event_arrive(event_type, logical_address, size, start_time) */ 92 | result = ssd -> event_arrive(WRITE, 6+i, 1, (double) 1800+(300*i)); 93 | printf("Write time: %.20lf\n", result); 94 | } 95 | 96 | // Force Merge 97 | result = ssd -> event_arrive(WRITE, 10 , 1, (double) 0.0); 98 | printf("Write time: %.20lf\n", result); 99 | // for (int i = 0; i < SIZE; i++) 100 | // { 101 | // /* event_arrive(event_type, logical_address, size, start_time) */ 102 | // result = ssd -> event_arrive(READ, i%64, 1, (double) 1+(500*i)); 103 | // printf("Read time : %.20lf\n", result); 104 | // } 105 | 106 | delete ssd; 107 | return 0; 108 | } 109 | -------------------------------------------------------------------------------- /run_test2.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2009, 2010 Brendan Tauras */ 2 | 3 | /* run_test2.cpp is part of FlashSim. */ 4 | 5 | /* FlashSim is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * any later version. */ 9 | 10 | /* FlashSim is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. */ 14 | 15 | /* You should have received a copy of the GNU General Public License 16 | * along with FlashSim. If not, see . */ 17 | 18 | /****************************************************************************/ 19 | 20 | /* Basic test driver 21 | * Brendan Tauras 2010-08-03 22 | * 23 | * driver to create and run a very basic test of writes then reads */ 24 | 25 | #include "ssd.h" 26 | 27 | #define SIZE 10 28 | 29 | using namespace ssd; 30 | 31 | int main() 32 | { 33 | load_config(); 34 | print_config(NULL); 35 | printf("Press ENTER to continue..."); 36 | getchar(); 37 | printf("\n"); 38 | 39 | Ssd *ssd = new Ssd(); 40 | 41 | double result; 42 | double cur_time = 1; 43 | double delta = BUS_DATA_DELAY - 2 > 0 ? BUS_DATA_DELAY - 2 : BUS_DATA_DELAY; 44 | 45 | for (int i = 0; i < SIZE; i++, cur_time += delta) 46 | { 47 | /* event_arrive(event_type, logical_address, size, start_time) */ 48 | result = ssd -> event_arrive(WRITE, i, 1, cur_time); 49 | result = ssd -> event_arrive(WRITE, i+10240, 1, cur_time); 50 | } 51 | for (int i = 0; i < SIZE; i++, cur_time += delta) 52 | { 53 | /* event_arrive(event_type, logical_address, size, start_time) */ 54 | result = ssd -> event_arrive(READ, 1, 1, cur_time); 55 | result = ssd -> event_arrive(READ, i, 1, cur_time); 56 | } 57 | delete ssd; 58 | return 0; 59 | } 60 | -------------------------------------------------------------------------------- /run_ufliptrace.cpp: -------------------------------------------------------------------------------- 1 | /* FlashSim is free software: you can redistribute it and/or modify 2 | * it under the terms of the GNU General Public License as published by 3 | * the Free Software Foundation, either version 3 of the License, or 4 | * any later version. */ 5 | 6 | /* FlashSim is distributed in the hope that it will be useful, 7 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU General Public License for more details. */ 10 | 11 | /* You should have received a copy of the GNU General Public License 12 | * along with FlashSim. If not, see . */ 13 | 14 | /****************************************************************************/ 15 | 16 | 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "ssd.h" 27 | 28 | using namespace ssd; 29 | 30 | int main(int argc, char **argv){ 31 | 32 | long vaddr; 33 | ssd::uint queryTime; 34 | char ioPatternType; // (S)equential or (R)andom 35 | char ioType; // (R)ead or (W)rite 36 | double arrive_time; 37 | int ioSize; 38 | 39 | char line[80]; 40 | 41 | double afterFormatStartTime = 0; 42 | 43 | load_config(); 44 | print_config(NULL); 45 | 46 | Ssd ssd; 47 | 48 | printf("INITIALIZING SSD\n"); 49 | 50 | int preIO = SSD_SIZE * PACKAGE_SIZE * DIE_SIZE * PLANE_SIZE * BLOCK_SIZE; 51 | 52 | if (FTL_IMPLEMENTATION == 0) // PAGE 53 | preIO -= 16*BLOCK_SIZE; 54 | 55 | if (FTL_IMPLEMENTATION == 1) // BAST 56 | preIO -= (BAST_LOG_BLOCK_LIMIT*BLOCK_SIZE)*1.2; 57 | 58 | if (FTL_IMPLEMENTATION == 2) // FAST 59 | preIO -= (FAST_LOG_BLOCK_LIMIT*BLOCK_SIZE)*1.1; 60 | 61 | if (FTL_IMPLEMENTATION > 2) // DFTL BIFTL 62 | preIO -= 1000; 63 | 64 | //int deviceSize = 2827059; 65 | int deviceSize = 2097024; 66 | 67 | if (preIO > deviceSize) 68 | preIO = deviceSize; 69 | 70 | printf("Writes %i pages for startup out of %i total pages.\n", preIO, SSD_SIZE * PACKAGE_SIZE * DIE_SIZE * PLANE_SIZE * BLOCK_SIZE); 71 | 72 | // srand(1); 73 | // for (int i=0; i files; 92 | struct dirent *dirp; 93 | while ((dirp = readdir(working_directory)) != NULL) 94 | { 95 | if (dirp->d_type == DT_REG) 96 | files.push_back(dirp->d_name); 97 | } 98 | 99 | std::sort(files.begin(), files.end()); 100 | 101 | double start_time = afterFormatStartTime; 102 | double timeMultiplier = 10000; 103 | 104 | 105 | long writeEvent = 0; 106 | long readEvent = 0; 107 | for (unsigned int i=0; i. 17 | 18 | ############################################################################## 19 | 20 | # ssd.conf 21 | # FlashSim configuration file 22 | # default values in ssd_config.cpp as used if value is not set in config file 23 | 24 | # Ram class: 25 | # delay to read from and write to the RAM for 1 page of data 26 | RAM_READ_DELAY 0.01 27 | RAM_WRITE_DELAY 0.01 28 | 29 | # Bus class: 30 | # delay to communicate over bus 31 | # max number of connected devices allowed 32 | # number of time entries bus has to keep track of future schedule usage 33 | # number of simultaneous communication channels - defined by SSD_SIZE 34 | BUS_CTRL_DELAY 2 35 | BUS_DATA_DELAY 10 36 | BUS_MAX_CONNECT 8 37 | BUS_TABLE_SIZE 512 38 | 39 | # Ssd class: 40 | # number of Packages per Ssd (size) 41 | SSD_SIZE 1 42 | 43 | # Package class: 44 | # number of Dies per Package (size) 45 | PACKAGE_SIZE 2 46 | 47 | # Die class: 48 | # number of Planes per Die (size) 49 | DIE_SIZE 2 50 | 51 | # Plane class: 52 | # number of Blocks per Plane (size) 53 | # delay for reading from plane register 54 | # delay for writing to plane register 55 | # delay for merging is based on read, write, reg_read, reg_write 56 | # and does not need to be explicitly defined 57 | PLANE_SIZE 256 58 | PLANE_REG_READ_DELAY 0.01 59 | PLANE_REG_WRITE_DELAY 0.01 60 | 61 | # Block class: 62 | # number of Pages per Block (size) 63 | # number of erases in lifetime of block 64 | # delay for erasing block 65 | BLOCK_SIZE 64 66 | BLOCK_ERASES 100000 67 | BLOCK_ERASE_DELAY 2000 68 | 69 | # Page class: 70 | # delay for Page reads 71 | # delay for Page writes 72 | # -- A 64bit kernel is required if data pages are used. -- 73 | # Allocate actual data for pages 74 | # Size of pages (in bytes) 75 | PAGE_READ_DELAY 25 76 | PAGE_WRITE_DELAY 300 77 | PAGE_ENABLE_DATA 1 78 | 79 | # MAPPING 80 | # Specify reservation of 81 | # blocks for mapping purposes. 82 | MAP_DIRECTORY_SIZE 100 83 | 84 | # FTL Implementation to use 0 = Page, 1 = BAST, 85 | # 2 = FAST, 3 = DFTL, 4 = Bimodal 86 | FTL_IMPLEMENTATION 3 87 | 88 | # LOG Block limit for BAST 89 | BAST_LOG_BLOCK_LIMIT 1024 90 | 91 | # LOG Block limit for FAST 92 | FAST_LOG_BLOCK_LIMIT 1024 93 | 94 | # Number of pages allowed to be in DFTL Cached Mapping Table. 95 | CACHE_DFTL_LIMIT 512 96 | 97 | # 0 -> Normal behavior, 1 -> Striping, 2 -> Logical address space parallelism 98 | PARALLELISM_MODE 2 99 | 100 | # Written in round robin: Virtual block size (as a multiple of the physical block size) 101 | VIRTUAL_BLOCK_SIZE 1 102 | 103 | # Striping: Virtual page size (as a multiple of the physical page size) 104 | VIRTUAL_PAGE_SIZE 1 105 | 106 | # RAISSDs: Number of physical SSDs 107 | RAID_NUMBER_OF_PHYSICAL_SSDS 2 108 | 109 | -------------------------------------------------------------------------------- /ssd.conf.testing: -------------------------------------------------------------------------------- 1 | # Copyright 2009, 2010 Brendan Tauras 2 | 3 | # ssd.conf.testing is part of FlashSim. 4 | 5 | # FlashSim is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # any later version. 9 | 10 | # FlashSim is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | 15 | # You should have received a copy of the GNU General Public License 16 | # along with FlashSim. If not, see . 17 | 18 | ############################################################################## 19 | 20 | # ssd.conf 21 | # FlashSim configuration file 22 | # default values in ssd_config.cpp as used if value is not set in config file 23 | 24 | 25 | # Ram class: 26 | # delay to read from and write to the RAM for 1 page of data 27 | RAM_READ_DELAY 1 28 | RAM_WRITE_DELAY 8 29 | 30 | # Bus class: 31 | # delay to communicate over bus 32 | # max number of connected devices allowed 33 | # number of time entries bus has to keep track of future schedule usage 34 | # number of simultaneous communication channels - defined by SSD_SIZE 35 | BUS_CTRL_DELAY 10 36 | BUS_DATA_DELAY 20 37 | BUS_MAX_CONNECT 8 38 | BUS_TABLE_SIZE 64 39 | 40 | # Ssd class: 41 | # number of Packages per Ssd (size) 42 | SSD_SIZE 4 43 | 44 | # Package class: 45 | # number of Dies per Package (size) 46 | PACKAGE_SIZE 8 47 | 48 | # Die class: 49 | # number of Planes per Die (size) 50 | DIE_SIZE 2 51 | 52 | # Plane class: 53 | # number of Blocks per Plane (size) 54 | # delay for reading from plane register 55 | # delay for writing to plane register 56 | # delay for merging is based on read, write, reg_read, reg_write 57 | # and does not need to be explicitly defined 58 | PLANE_SIZE 64 59 | PLANE_REG_READ_DELAY 1 60 | PLANE_REG_WRITE_DELAY 1 61 | 62 | # Block class: 63 | # number of Pages per Block (size) 64 | # number of erases in lifetime of block 65 | # delay for erasing block 66 | BLOCK_SIZE 16 67 | BLOCK_ERASES 1048675 68 | BLOCK_ERASE_DELAY 32 69 | 70 | # Page class: 71 | # delay for Page reads 72 | # delay for Page writes 73 | PAGE_READ_DELAY 4 74 | PAGE_WRITE_DELAY 12 75 | -------------------------------------------------------------------------------- /ssd_address.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2009, 2010 Brendan Tauras */ 2 | 3 | /* ssd_address.cpp is part of FlashSim. */ 4 | 5 | /* FlashSim is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * any later version. */ 9 | 10 | /* FlashSim is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. */ 14 | 15 | /* You should have received a copy of the GNU General Public License 16 | * along with FlashSim. If not, see . */ 17 | 18 | /****************************************************************************/ 19 | 20 | /* Address class 21 | * Brendan Tauras 2009-06-19 22 | * 23 | * Class to manage physical addresses for the SSD. It was designed to have 24 | * public members like a struct for quick access but also have checking, 25 | * printing, and assignment functionality. An instance is created for each 26 | * physical address in the Event class. 27 | */ 28 | 29 | #include 30 | #include "ssd.h" 31 | 32 | using namespace ssd; 33 | 34 | Address::Address(void): 35 | package(0), 36 | die(0), 37 | plane(0), 38 | block(0), 39 | page(0), 40 | valid(NONE) 41 | { 42 | return; 43 | } 44 | 45 | Address::Address(const Address &address) 46 | { 47 | *this = address; 48 | return; 49 | } 50 | 51 | Address::Address(const Address *address) 52 | { 53 | *this = *address; 54 | return; 55 | } 56 | 57 | /* see "enum address_valid" in ssd.h for details on valid status */ 58 | Address::Address(uint package, uint die, uint plane, uint block, uint page, enum address_valid valid): 59 | package(package), 60 | die(die), 61 | plane(plane), 62 | block(block), 63 | page(page), 64 | valid(valid) 65 | { 66 | return; 67 | } 68 | 69 | Address::Address(uint address, enum address_valid valid): 70 | valid(valid) 71 | { 72 | assert(address >= 0); 73 | set_linear_address(address); 74 | } 75 | 76 | Address::~Address() 77 | { 78 | return; 79 | } 80 | 81 | /* default values for parameters are the global settings 82 | * see "enum address_valid" in ssd.h for details on valid status 83 | * note that method only checks for out-of-bounds types of errors */ 84 | enum address_valid Address::check_valid(uint ssd_size, uint package_size, uint die_size, uint plane_size, uint block_size) 85 | { 86 | enum address_valid tmp = NONE; 87 | 88 | /* must check current valid status first 89 | * so we cannot expand the valid status */ 90 | if(valid >= PACKAGE && package < ssd_size) 91 | { 92 | tmp = PACKAGE; 93 | if(valid >= DIE && die < package_size) 94 | { 95 | tmp = DIE; 96 | if(valid >= PLANE && plane < die_size) 97 | { 98 | tmp = PLANE; 99 | if(valid >= BLOCK && block < plane_size) 100 | { 101 | tmp = BLOCK; 102 | if(valid >= PAGE && page < block_size) 103 | tmp = PAGE; 104 | } 105 | } 106 | } 107 | } 108 | else 109 | tmp = NONE; 110 | valid = tmp; 111 | return valid; 112 | } 113 | 114 | /* returns enum indicating to what level two addresses match 115 | * limits comparison to the fields that are valid */ 116 | enum address_valid Address::compare(const Address &address) const 117 | { 118 | enum address_valid match = NONE; 119 | if(package == address.package && valid >= PACKAGE && address.valid >= PACKAGE) 120 | { 121 | match = PACKAGE; 122 | if(die == address.die && valid >= DIE && address.valid >= DIE) 123 | { 124 | match = DIE; 125 | if(plane == address.plane && valid >= PLANE && address.valid >= PLANE) 126 | { 127 | match = PLANE; 128 | if(block == address.block && valid >= BLOCK && address.valid >= BLOCK) 129 | { 130 | match = BLOCK; 131 | if(page == address.page && valid >= PAGE && address.valid >= PAGE) 132 | { 133 | match = PAGE; 134 | } 135 | } 136 | } 137 | } 138 | } 139 | return match; 140 | } 141 | 142 | /* default stream is stdout */ 143 | void Address::print(FILE *stream) 144 | { 145 | fprintf(stream, "(%d, %d, %d, %d, %d, %d)", package, die, plane, block, page, (int) valid); 146 | return; 147 | } 148 | 149 | void Address::set_linear_address(ulong address) 150 | { 151 | real_address = address; 152 | page = address % BLOCK_SIZE; 153 | address /= BLOCK_SIZE; 154 | block = address % PLANE_SIZE; 155 | address /= PLANE_SIZE; 156 | plane = address % DIE_SIZE; 157 | address /= DIE_SIZE; 158 | die = address % PACKAGE_SIZE; 159 | address /= PACKAGE_SIZE; 160 | package = address % SSD_SIZE; 161 | address /= SSD_SIZE; 162 | } 163 | 164 | void Address::set_linear_address(ulong address, enum address_valid valid) 165 | { 166 | set_linear_address(address); 167 | this->valid = valid; 168 | } 169 | 170 | unsigned long Address::get_linear_address() const 171 | { 172 | return real_address; 173 | } 174 | 175 | void Address::operator+(int i) 176 | { 177 | set_linear_address(real_address + i); 178 | } 179 | 180 | void Address::operator+(uint i) 181 | { 182 | set_linear_address(real_address + i); 183 | } 184 | 185 | Address &Address::operator+=(const uint i) 186 | { 187 | set_linear_address(real_address + i); 188 | return *this; 189 | } 190 | 191 | 192 | Address &Address::operator=(const Address &rhs) 193 | { 194 | if(this == &rhs) 195 | return *this; 196 | package = rhs.package; 197 | die = rhs.die; 198 | plane = rhs.plane; 199 | block = rhs.block; 200 | page = rhs.page; 201 | valid = rhs.valid; 202 | real_address = rhs.real_address; 203 | return *this; 204 | } 205 | -------------------------------------------------------------------------------- /ssd_block.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2009, 2010 Brendan Tauras */ 2 | 3 | /* ssd_block.cpp is part of FlashSim. */ 4 | 5 | /* FlashSim is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * any later version. */ 9 | 10 | /* FlashSim is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. */ 14 | 15 | /* You should have received a copy of the GNU General Public License 16 | * along with FlashSim. If not, see . */ 17 | 18 | /****************************************************************************/ 19 | 20 | /* Block class 21 | * Brendan Tauras 2009-10-26 22 | * 23 | * The block is the data storage hardware unit where erases are implemented. 24 | * Blocks maintain wear statistics for the FTL. */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include "ssd.h" 30 | 31 | using namespace ssd; 32 | 33 | Block::Block(const Plane &parent, uint block_size, ulong erases_remaining, double erase_delay, long physical_address): 34 | pages_invalid(0), 35 | physical_address(physical_address), 36 | size(block_size), 37 | 38 | /* use a const pointer (Page * const data) to use as an array 39 | * but like a reference, we cannot reseat the pointer */ 40 | data((Page *) malloc(block_size * sizeof(Page))), 41 | parent(parent), 42 | pages_valid(0), 43 | 44 | state(FREE), 45 | 46 | /* set erases remaining to BLOCK_ERASES to match Block constructor args 47 | * in Plane class 48 | * this is the cheap implementation but can change to pass through classes */ 49 | erases_remaining(erases_remaining), 50 | 51 | /* assume hardware created at time 0 and had an implied free erasure */ 52 | last_erase_time(0.0), 53 | erase_delay(erase_delay), 54 | 55 | modification_time(-1) 56 | 57 | { 58 | uint i; 59 | 60 | if(erase_delay < 0.0) 61 | { 62 | fprintf(stderr, "Block warning: %s: constructor received negative erase delay value\n\tsetting erase delay to 0.0\n", __func__); 63 | erase_delay = 0.0; 64 | } 65 | 66 | /* new cannot initialize an array with constructor args so 67 | * malloc the array 68 | * then use placement new to call the constructor for each element 69 | * chose an array over container class so we don't have to rely on anything 70 | * i.e. STL's std::vector */ 71 | /* array allocated in initializer list: 72 | * data = (Page *) malloc(size * sizeof(Page)); */ 73 | if(data == NULL){ 74 | fprintf(stderr, "Block error: %s: constructor unable to allocate Page data\n", __func__); 75 | exit(MEM_ERR); 76 | } 77 | 78 | for(i = 0; i < size; i++) 79 | (void) new (&data[i]) Page(*this, PAGE_READ_DELAY, PAGE_WRITE_DELAY); 80 | 81 | // Creates the active cost structure in the block manager. 82 | // It assumes that it is created lineary. 83 | Block_manager::instance()->cost_insert(this); 84 | 85 | return; 86 | } 87 | 88 | Block::~Block(void) 89 | { 90 | assert(data != NULL); 91 | uint i; 92 | /* call destructor for each Page array element 93 | * since we used malloc and placement new */ 94 | for(i = 0; i < size; i++) 95 | data[i].~Page(); 96 | free(data); 97 | return; 98 | } 99 | 100 | enum status Block::read(Event &event) 101 | { 102 | assert(data != NULL); 103 | return data[event.get_address().page]._read(event); 104 | } 105 | 106 | enum status Block::write(Event &event) 107 | { 108 | assert(data != NULL); 109 | enum status ret = data[event.get_address().page]._write(event); 110 | 111 | if(event.get_noop() == false) 112 | { 113 | pages_valid++; 114 | state = ACTIVE; 115 | modification_time = event.get_start_time(); 116 | 117 | Block_manager::instance()->update_block(this); 118 | } 119 | return ret; 120 | } 121 | 122 | enum status Block::replace(Event &event) 123 | { 124 | invalidate_page(event.get_replace_address().page); 125 | return SUCCESS; 126 | } 127 | 128 | /* updates Event time_taken 129 | * sets Page statuses to EMPTY 130 | * updates last_erase_time and erases_remaining 131 | * returns 1 for success, 0 for failure */ 132 | enum status Block::_erase(Event &event) 133 | { 134 | assert(data != NULL && erase_delay >= 0.0); 135 | uint i; 136 | 137 | if (!event.get_noop()) 138 | { 139 | if(erases_remaining < 1) 140 | { 141 | fprintf(stderr, "Block error: %s: No erases remaining when attempting to erase\n", __func__); 142 | return FAILURE; 143 | } 144 | 145 | for(i = 0; i < size; i++) 146 | { 147 | //assert(data[i].get_state() == INVALID); 148 | data[i].set_state(EMPTY); 149 | } 150 | 151 | 152 | event.incr_time_taken(erase_delay); 153 | last_erase_time = event.get_start_time() + event.get_time_taken(); 154 | erases_remaining--; 155 | pages_valid = 0; 156 | pages_invalid = 0; 157 | state = FREE; 158 | 159 | Block_manager::instance()->update_block(this); 160 | } 161 | 162 | return SUCCESS; 163 | } 164 | 165 | const Plane &Block::get_parent(void) const 166 | { 167 | return parent; 168 | } 169 | 170 | ssd::uint Block::get_pages_valid(void) const 171 | { 172 | return pages_valid; 173 | } 174 | 175 | ssd::uint Block::get_pages_invalid(void) const 176 | { 177 | return pages_invalid; 178 | } 179 | 180 | 181 | enum block_state Block::get_state(void) const 182 | { 183 | return state; 184 | } 185 | 186 | enum page_state Block::get_state(uint page) const 187 | { 188 | assert(data != NULL && page < size); 189 | return data[page].get_state(); 190 | } 191 | 192 | enum page_state Block::get_state(const Address &address) const 193 | { 194 | assert(data != NULL && address.page < size && address.valid >= BLOCK); 195 | return data[address.page].get_state(); 196 | } 197 | 198 | double Block::get_last_erase_time(void) const 199 | { 200 | return last_erase_time; 201 | } 202 | 203 | ssd::ulong Block::get_erases_remaining(void) const 204 | { 205 | return erases_remaining; 206 | } 207 | 208 | ssd::uint Block::get_size(void) const 209 | { 210 | return size; 211 | } 212 | 213 | void Block::invalidate_page(uint page) 214 | { 215 | assert(page < size); 216 | 217 | if (data[page].get_state() == INVALID ) 218 | return; 219 | 220 | //assert(data[page].get_state() == VALID); 221 | 222 | data[page].set_state(INVALID); 223 | 224 | pages_invalid++; 225 | 226 | Block_manager::instance()->update_block(this); 227 | 228 | /* update block state */ 229 | if(pages_invalid >= size) 230 | state = INACTIVE; 231 | else if(pages_valid > 0 || pages_invalid > 0) 232 | state = ACTIVE; 233 | else 234 | state = FREE; 235 | 236 | return; 237 | } 238 | 239 | double Block::get_modification_time(void) const 240 | { 241 | return modification_time; 242 | } 243 | 244 | /* method to find the next usable (empty) page in this block 245 | * method is called by write and erase methods and in Plane::get_next_page() */ 246 | enum status Block::get_next_page(Address &address) const 247 | { 248 | uint i; 249 | 250 | for(i = 0; i < size; i++) 251 | { 252 | if(data[i].get_state() == EMPTY) 253 | { 254 | address.set_linear_address(i + physical_address - physical_address % BLOCK_SIZE, PAGE); 255 | return SUCCESS; 256 | } 257 | } 258 | return FAILURE; 259 | } 260 | 261 | long Block::get_physical_address(void) const 262 | { 263 | return physical_address; 264 | } 265 | 266 | Block *Block::get_pointer(void) 267 | { 268 | return this; 269 | } 270 | 271 | block_type Block::get_block_type(void) const 272 | { 273 | return this->btype; 274 | } 275 | 276 | void Block::set_block_type(block_type value) 277 | { 278 | this->btype = value; 279 | } 280 | -------------------------------------------------------------------------------- /ssd_bm.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2011 Matias Bjørling */ 2 | 3 | /* Block Management 4 | * 5 | * This class handle allocation of block pools for the FTL 6 | * algorithms. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "ssd.h" 17 | 18 | using namespace ssd; 19 | 20 | 21 | Block_manager::Block_manager(FtlParent *ftl) : ftl(ftl) 22 | { 23 | /* 24 | * Configuration of blocks. 25 | * User-space is the number of blocks minus the 26 | * requirements for map directory. 27 | */ 28 | 29 | max_blocks = NUMBER_OF_ADDRESSABLE_BLOCKS; 30 | max_log_blocks = max_blocks; 31 | 32 | if (FTL_IMPLEMENTATION == IMPL_FAST) 33 | max_log_blocks = FAST_LOG_BLOCK_LIMIT; 34 | 35 | // Block-based map lookup simulation 36 | max_map_pages = MAP_DIRECTORY_SIZE * BLOCK_SIZE; 37 | 38 | directoryCurrentPage = 0; 39 | num_insert_events = 0; 40 | 41 | data_active = 0; 42 | log_active = 0; 43 | 44 | current_writing_block = -2; 45 | 46 | out_of_blocks = false; 47 | 48 | simpleCurrentFree = 0; 49 | 50 | active_cost.reserve(NUMBER_OF_ADDRESSABLE_BLOCKS); 51 | } 52 | 53 | Block_manager::~Block_manager(void) 54 | { 55 | return; 56 | } 57 | 58 | void Block_manager::cost_insert(Block *b) 59 | { 60 | active_cost.push_back(b); 61 | } 62 | 63 | void Block_manager::instance_initialize(FtlParent *ftl) 64 | { 65 | Block_manager::inst = new Block_manager(ftl); 66 | } 67 | 68 | Block_manager *Block_manager::instance() 69 | { 70 | return Block_manager::inst; 71 | } 72 | 73 | /* 74 | * Retrieves a page using either simple approach (when not all 75 | * pages have been written or the complex that retrieves 76 | * it from a free page list. 77 | */ 78 | void Block_manager::get_page_block(Address &address, Event &event) 79 | { 80 | // We need separate queues for each plane? communication channel? communication channel is at the per die level at the moment. i.e. each LUN is a die. 81 | 82 | if (simpleCurrentFree < max_blocks*BLOCK_SIZE) 83 | { 84 | address.set_linear_address(simpleCurrentFree, BLOCK); 85 | current_writing_block = simpleCurrentFree; 86 | simpleCurrentFree += BLOCK_SIZE; 87 | } 88 | else 89 | { 90 | if (free_list.size() <= 1 && !out_of_blocks) 91 | { 92 | out_of_blocks = true; 93 | insert_events(event); 94 | } 95 | 96 | assert(free_list.size() != 0); 97 | address.set_linear_address(free_list.front()->get_physical_address(), BLOCK); 98 | current_writing_block = free_list.front()->get_physical_address(); 99 | free_list.erase(free_list.begin()); 100 | out_of_blocks = false; 101 | } 102 | } 103 | 104 | 105 | Address Block_manager::get_free_block(Event &event) 106 | { 107 | return get_free_block(DATA, event); 108 | } 109 | 110 | /* 111 | * Handles block manager statistics when changing a 112 | * block to a data block from a log block or vice versa. 113 | */ 114 | void Block_manager::promote_block(block_type to_type) 115 | { 116 | if (to_type == DATA) 117 | { 118 | data_active++; 119 | log_active--; 120 | } 121 | else if (to_type == LOG) 122 | { 123 | log_active++; 124 | data_active--; 125 | } 126 | } 127 | 128 | /* 129 | * Returns true if there are no space left for additional log pages. 130 | */ 131 | bool Block_manager::is_log_full() 132 | { 133 | return log_active == max_log_blocks; 134 | } 135 | 136 | void Block_manager::print_statistics() 137 | { 138 | printf("-----------------\n"); 139 | printf("Block Statistics:\n"); 140 | printf("-----------------\n"); 141 | printf("Log blocks: %lu\n", log_active); 142 | printf("Data blocks: %lu\n", data_active); 143 | printf("Free blocks: %lu\n", (max_blocks - (simpleCurrentFree/BLOCK_SIZE)) + free_list.size()); 144 | printf("Invalid blocks: %lu\n", invalid_list.size()); 145 | printf("Free2 blocks: %lu\n", (unsigned long int)invalid_list.size() + (unsigned long int)log_active + (unsigned long int)data_active - (unsigned long int)free_list.size()); 146 | printf("-----------------\n"); 147 | 148 | 149 | } 150 | 151 | void Block_manager::invalidate(Address address, block_type type) 152 | { 153 | invalid_list.push_back(ftl->get_block_pointer(address)); 154 | 155 | switch (type) 156 | { 157 | case DATA: 158 | data_active--; 159 | break; 160 | case LOG: 161 | log_active--; 162 | break; 163 | case LOG_SEQ: 164 | break; 165 | } 166 | } 167 | 168 | /* 169 | * Insert erase events into the event stream. 170 | * The strategy is to clean up all invalid pages instantly. 171 | */ 172 | void Block_manager::insert_events(Event &event) 173 | { 174 | // Calculate if GC should be activated. 175 | float used = (int)invalid_list.size() + (int)log_active + (int)data_active - (int)free_list.size(); 176 | float total = NUMBER_OF_ADDRESSABLE_BLOCKS; 177 | float ratio = used/total; 178 | 179 | if (ratio < 0.90) // Magic number 180 | return; 181 | 182 | uint num_to_erase = 5; // More Magic! 183 | 184 | //printf("%i %i %i\n", invalid_list.size(), log_active, data_active); 185 | 186 | // First step and least expensive is to go though invalid list. (Only used by FAST) 187 | while (num_to_erase != 0 && invalid_list.size() != 0) 188 | { 189 | Event erase_event = Event(ERASE, event.get_logical_address(), 1, event.get_start_time()); 190 | erase_event.set_address(Address(invalid_list.back()->get_physical_address(), BLOCK)); 191 | if (ftl->controller.issue(erase_event) == FAILURE) { assert(false);} 192 | event.incr_time_taken(erase_event.get_time_taken()); 193 | 194 | free_list.push_back(invalid_list.back()); 195 | invalid_list.pop_back(); 196 | 197 | num_to_erase--; 198 | ftl->controller.stats.numFTLErase++; 199 | } 200 | 201 | num_insert_events++; 202 | 203 | if (FTL_IMPLEMENTATION == IMPL_DFTL || FTL_IMPLEMENTATION == IMPL_BIMODAL) 204 | { 205 | 206 | ActiveByCost::iterator it = active_cost.get<1>().end(); 207 | --it; 208 | 209 | 210 | while (num_to_erase != 0 && (*it)->get_pages_invalid() > 0 && (*it)->get_pages_valid() == BLOCK_SIZE) 211 | { 212 | if (current_writing_block != (*it)->physical_address) 213 | { 214 | //printf("erase p: %p phy: %li ratio: %i num: %i\n", (*it), (*it)->physical_address, (*it)->get_pages_invalid(), num_to_erase); 215 | Block *blockErase = (*it); 216 | 217 | // Let the FTL handle cleanup of the block. 218 | ftl->cleanup_block(event, blockErase); 219 | 220 | // Create erase event and attach to current event queue. 221 | Event erase_event = Event(ERASE, event.get_logical_address(), 1, event.get_start_time()); 222 | erase_event.set_address(Address(blockErase->get_physical_address(), BLOCK)); 223 | 224 | // Execute erase 225 | if (ftl->controller.issue(erase_event) == FAILURE) { assert(false); } 226 | 227 | free_list.push_back(blockErase); 228 | 229 | event.incr_time_taken(erase_event.get_time_taken()); 230 | 231 | ftl->controller.stats.numFTLErase++; 232 | } 233 | 234 | it = active_cost.get<1>().end(); 235 | --it; 236 | 237 | if (current_writing_block == (*it)->physical_address) 238 | --it; 239 | 240 | num_to_erase--; 241 | } 242 | } 243 | } 244 | 245 | Address Block_manager::get_free_block(block_type type, Event &event) 246 | { 247 | Address address; 248 | get_page_block(address, event); 249 | switch (type) 250 | { 251 | case DATA: 252 | ftl->controller.get_block_pointer(address)->set_block_type(DATA); 253 | data_active++; 254 | break; 255 | case LOG: 256 | if (log_active > max_log_blocks) 257 | throw std::bad_alloc(); 258 | 259 | ftl->controller.get_block_pointer(address)->set_block_type(LOG); 260 | log_active++; 261 | break; 262 | default: 263 | break; 264 | } 265 | 266 | return address; 267 | } 268 | 269 | void Block_manager::print_cost_status() 270 | { 271 | 272 | ActiveByCost::iterator it = active_cost.get<1>().begin(); 273 | 274 | for (uint i=0;i<10;i++) //SSD_SIZE*PACKAGE_SIZE*DIE_SIZE*PLANE_SIZE 275 | { 276 | printf("%li %i %i\n", (*it)->physical_address, (*it)->get_pages_valid(), (*it)->get_pages_invalid()); 277 | ++it; 278 | } 279 | 280 | printf("end:::\n"); 281 | 282 | it = active_cost.get<1>().end(); 283 | --it; 284 | 285 | for (uint i=0;i<10;i++) //SSD_SIZE*PACKAGE_SIZE*DIE_SIZE*PLANE_SIZE 286 | { 287 | printf("%li %i %i\n", (*it)->physical_address, (*it)->get_pages_valid(), (*it)->get_pages_invalid()); 288 | --it; 289 | } 290 | } 291 | 292 | void Block_manager::erase_and_invalidate(Event &event, Address &address, block_type btype) 293 | { 294 | Event erase_event = Event(ERASE, event.get_logical_address(), 1, event.get_start_time()+event.get_time_taken()); 295 | erase_event.set_address(address); 296 | 297 | if (ftl->controller.issue(erase_event) == FAILURE) { assert(false);} 298 | 299 | free_list.push_back(ftl->get_block_pointer(address)); 300 | 301 | switch (btype) 302 | { 303 | case DATA: 304 | data_active--; 305 | break; 306 | case LOG: 307 | log_active--; 308 | break; 309 | case LOG_SEQ: 310 | break; 311 | } 312 | 313 | event.incr_time_taken(erase_event.get_time_taken()); 314 | ftl->controller.stats.numFTLErase++; 315 | } 316 | 317 | int Block_manager::get_num_free_blocks() 318 | { 319 | if (simpleCurrentFree < max_blocks*BLOCK_SIZE) 320 | return (simpleCurrentFree / BLOCK_SIZE) + free_list.size(); 321 | else 322 | return free_list.size(); 323 | } 324 | 325 | void Block_manager::update_block(Block * b) 326 | { 327 | std::size_t pos = (b->physical_address / BLOCK_SIZE); 328 | active_cost.replace(active_cost.begin()+pos, b); 329 | } 330 | -------------------------------------------------------------------------------- /ssd_bus.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2009, 2010 Brendan Tauras */ 2 | 3 | /* ssd_bus.cpp is part of FlashSim. */ 4 | 5 | /* FlashSim is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * any later version. */ 9 | 10 | /* FlashSim is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. */ 14 | 15 | /* You should have received a copy of the GNU General Public License 16 | * along with FlashSim. If not, see . */ 17 | 18 | /****************************************************************************/ 19 | 20 | /* Bus class 21 | * Brendan Tauras 2009-04-06 22 | * 23 | * Multi-channel bus comprised of Channel class objects 24 | * Simulates control and data delays by allowing variable channel lock 25 | * durations. The sender (controller class) should specify the delay (control, 26 | * data, or both) for events (i.e. read = ctrl, ctrl+data; write = ctrl+data; 27 | * erase or merge = ctrl). The hardware enable signals are implicitly 28 | * simulated by the sender locking the appropriate bus channel through the lock 29 | * method, then sending to multiple devices by calling the appropriate method 30 | * in the Package class. */ 31 | 32 | #include 33 | #include 34 | #include 35 | #include "ssd.h" 36 | 37 | using namespace ssd; 38 | 39 | /* a multi-channel bus: multiple independent channels that operate in parallel 40 | * allocate channels and pass parameters to channels via the lock method 41 | * the table size is synonymous to the queue size for each separate channel 42 | * it is not necessary to use the max connections properly, but it is provided 43 | * to help ensure correctness */ 44 | Bus::Bus(uint num_channels, double ctrl_delay, double data_delay, uint table_size, uint max_connections): 45 | num_channels(num_channels), 46 | 47 | /* use a const pointer (Channel * const channels) to use as an array 48 | * but like a reference, we cannot reseat the pointer */ 49 | channels((Channel *) malloc(num_channels * sizeof(Channel))) 50 | { 51 | assert(table_size > 0); 52 | if(ctrl_delay < 0.0){ 53 | fprintf(stderr, "Bus warning: %s: constructor received negative control delay value\n\tsetting control delay to 0.0\n", __func__); 54 | ctrl_delay = 0.0; 55 | } 56 | if(data_delay < 0.0){ 57 | fprintf(stderr, "Bus warning: %s: constructor received negative data delay value\n\tsetting data delay to 0.0\n", __func__); 58 | data_delay = 0.0; 59 | } 60 | uint i; 61 | 62 | /* allocate channels */ 63 | /* new cannot initialize an array with constructor args 64 | * malloc the array 65 | * then use placement new to call the constructor for each element 66 | * chose an array over container class so we don't have to rely on anything 67 | * i.e. STL's std::vector */ 68 | /* array allocated in initializer list: 69 | * channels = (Channel *) malloc(num_channels * sizeof(Channel)); */ 70 | if(channels == NULL) 71 | { 72 | fprintf(stderr, "Bus error: %s: constructor unable to allocate Channels\n", __func__); 73 | exit(MEM_ERR); 74 | } 75 | for(i = 0; i < num_channels; i++) 76 | (void) new (&channels[i]) Channel(ctrl_delay, data_delay, table_size, max_connections); 77 | 78 | return; 79 | } 80 | 81 | /* deallocate channels */ 82 | Bus::~Bus(void) 83 | { 84 | assert(channels != NULL); 85 | uint i; 86 | for(i = 0; i < num_channels; i++) 87 | channels[i].~Channel(); 88 | free(channels); 89 | return; 90 | } 91 | 92 | /* not required before calling lock() 93 | * but should be used to help ensure correctness 94 | * controller that talks on all channels should not connect/disconnect 95 | * only devices that use a channel should connect/disconnect */ 96 | enum status Bus::connect(uint channel) 97 | { 98 | assert(channels != NULL && channel < num_channels); 99 | return channels[channel].connect(); 100 | } 101 | 102 | /* not required when finished 103 | * but should be used to help ensure correctness 104 | * controller that talks on all channels should not connect/disconnect 105 | * only devices that use a channel should connect/disconnect */ 106 | enum status Bus::disconnect(uint channel) 107 | { 108 | assert(channels != NULL && channel < num_channels); 109 | return channels[channel].disconnect(); 110 | } 111 | 112 | /* lock bus channel for event 113 | * updates event with bus delay and bus wait time if there is wait time 114 | * channel will automatically unlock after event is finished using bus 115 | * assumes event is sent across channel as soon as bus is available 116 | * event may fail if channel is saturated so check return value 117 | */ 118 | enum status Bus::lock(uint channel, double start_time, double duration, Event &event) 119 | { 120 | assert(channels != NULL && start_time >= 0.0 && duration > 0.0); 121 | return channels[channel].lock(start_time, duration, event); 122 | } 123 | 124 | Channel &Bus::get_channel(uint channel) 125 | { 126 | assert(channels != NULL && channel < num_channels); 127 | return channels[channel]; 128 | } 129 | 130 | double Bus::ready_time(uint channel) 131 | { 132 | assert(channels != NULL && channel < num_channels); 133 | return channels[channel].ready_time(); 134 | } 135 | -------------------------------------------------------------------------------- /ssd_channel.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2009, 2010 Brendan Tauras */ 2 | 3 | /* ssd_channel.cpp is part of FlashSim. */ 4 | 5 | /* FlashSim is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * any later version. */ 9 | 10 | /* FlashSim is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. */ 14 | 15 | /* You should have received a copy of the GNU General Public License 16 | * along with FlashSim. If not, see . */ 17 | 18 | /****************************************************************************/ 19 | 20 | /* Channel class 21 | * Brendan Tauras 2010-08-09 22 | * 23 | * Single bus channel 24 | * Simulate multiple devices on 1 bus channel with variable bus transmission 25 | * durations for data and control delays with the Channel class. Provide the 26 | * delay times to send a control signal or 1 page of data across the bus 27 | * channel, the bus table size for the maximum number channel transmissions that 28 | * can be queued, and the maximum number of devices that can connect to the bus. 29 | * To elaborate, the table size is the size of the channel scheduling table that 30 | * holds start and finish times of events that have not yet completed in order 31 | * to determine where the next event can be scheduled for bus utilization. 32 | */ 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include "ssd.h" 40 | 41 | using namespace ssd; 42 | 43 | /* a single channel bus: all connected devices share the same channel 44 | * simulates control and data 45 | * enable signals are implicitly simulated by the sender locking the bus 46 | * then sending to multiple devices 47 | * the table size is synonymous to the queue size for the channel 48 | * it is not necessary to use the max connections properly, but it is provided 49 | * to help ensure correctness */ 50 | Channel::Channel(double ctrl_delay, double data_delay, uint table_size, uint max_connections): 51 | //table_size(table_size), 52 | 53 | /* use a const pointer (double * const) for the scheduling table arrays 54 | * like a reference, we cannot reseat the pointer */ 55 | table_entries(0), 56 | selected_entry(0), 57 | num_connected(0), 58 | max_connections(max_connections), 59 | ctrl_delay(ctrl_delay), 60 | data_delay(data_delay) 61 | { 62 | if(ctrl_delay < 0.0){ 63 | fprintf(stderr, "Bus channel warning: %s: constructor received negative control delay value\n\tsetting control delay to 0.0\n", __func__); 64 | ctrl_delay = 0.0; 65 | } 66 | if(data_delay < 0.0){ 67 | fprintf(stderr, "Bus channel warning: %s: constructor received negative data delay value\n\tsetting data delay to 0.0\n", __func__); 68 | data_delay = 0.0; 69 | } 70 | 71 | timings.reserve(4096); 72 | 73 | ready_at = -1; 74 | } 75 | 76 | /* free allocated bus channel state space */ 77 | Channel::~Channel(void) 78 | { 79 | if(num_connected > 0) 80 | fprintf(stderr, "Bus channel warning: %s: %d connected devices when bus channel terminated\n", __func__, num_connected); 81 | return; 82 | } 83 | 84 | /* not required before calling lock() 85 | * but should be used to help ensure correctness 86 | * controller that talks on all channels should not connect/disconnect 87 | * only components that receive a single channel should connect */ 88 | enum status Channel::connect(void) 89 | { 90 | if(num_connected < max_connections) 91 | { 92 | num_connected++; 93 | return SUCCESS; 94 | } 95 | else 96 | { 97 | fprintf(stderr, "Bus channel error: %s: device attempting to connect to channel when %d max devices already connected\n", __func__, max_connections); 98 | return FAILURE; 99 | } 100 | } 101 | 102 | /* not required when finished 103 | * but should be used to help ensure correctness 104 | * controller that talks on all channels should not connect/disconnect 105 | * only components that receive a single channel should connect */ 106 | enum status Channel::disconnect(void) 107 | { 108 | if(num_connected > 0) 109 | { 110 | num_connected--; 111 | return SUCCESS; 112 | } 113 | fprintf(stderr, "Bus channel error: %s: device attempting to disconnect from bus channel when no devices connected\n", __func__); 114 | return FAILURE; 115 | } 116 | 117 | /* lock bus channel for event 118 | * updates event with bus delay and bus wait time if there is wait time 119 | * bus will automatically unlock after event is finished using bus 120 | * event is sent across bus as soon as bus channel is available 121 | * event may fail if bus channel is saturated so check return value 122 | */ 123 | enum status Channel::lock(double start_time, double duration, Event &event) 124 | { 125 | assert(num_connected <= max_connections); 126 | assert(ctrl_delay >= 0.0); 127 | assert(data_delay >= 0.0); 128 | assert(start_time >= 0.0); 129 | assert(duration >= 0.0); 130 | 131 | /* free up any table slots and sort existing ones */ 132 | unlock(start_time); 133 | 134 | double sched_time = BUS_CHANNEL_FREE_FLAG; 135 | 136 | /* just schedule if table is empty */ 137 | if(timings.size() == 0) 138 | sched_time = start_time; 139 | 140 | /* check if can schedule before or in between before just scheduling 141 | * after all other events */ 142 | else 143 | { 144 | /* skip over empty table entries 145 | * empty table entries will be first from sorting (in unlock method) 146 | * because the flag is a negative value */ 147 | std::vector::iterator it = timings.begin(); 148 | 149 | /* schedule before first event in table */ 150 | if((*it).lock_time > start_time && (*it).lock_time - start_time >= duration) 151 | sched_time = start_time; 152 | 153 | /* schedule in between other events in table */ 154 | if(sched_time == BUS_CHANNEL_FREE_FLAG) 155 | { 156 | for(; it < timings.end(); it++) 157 | { 158 | if (it + 1 != timings.end()) 159 | { 160 | /* enough time to schedule in between next two events */ 161 | if((*it).unlock_time >= start_time && (*(it+1)).lock_time - (*it).unlock_time >= duration) 162 | { 163 | sched_time = (*it).unlock_time; 164 | break; 165 | } 166 | } 167 | 168 | } 169 | } 170 | 171 | /* schedule after all events in table */ 172 | if(sched_time == BUS_CHANNEL_FREE_FLAG) 173 | sched_time = timings.back().unlock_time; 174 | } 175 | 176 | /* write scheduling info in free table slot */ 177 | lock_times lt; 178 | lt.lock_time = sched_time; 179 | lt.unlock_time = sched_time + duration; 180 | timings.push_back(lt); 181 | 182 | if (lt.unlock_time > ready_at) 183 | ready_at = lt.unlock_time; 184 | 185 | /* update event times for bus wait and time taken */ 186 | event.incr_bus_wait_time(sched_time - start_time); 187 | event.incr_time_taken(sched_time - start_time + duration); 188 | 189 | return SUCCESS; 190 | } 191 | 192 | /* remove all expired entries (finish time is less than provided time) 193 | * update current number of table entries used 194 | * sort table by finish times (2nd row) */ 195 | void Channel::unlock(double start_time) 196 | { 197 | /* remove expired channel lock entries */ 198 | std::vector::iterator it; 199 | for ( it = timings.begin(); it < timings.end();) 200 | { 201 | if((*it).unlock_time <= start_time) 202 | timings.erase(it); 203 | else 204 | { 205 | it++; 206 | } 207 | } 208 | std::sort(timings.begin(), timings.end(), &timings_sorter); 209 | } 210 | 211 | bool Channel::timings_sorter(lock_times const& lhs, lock_times const& rhs) { 212 | return lhs.lock_time < rhs.lock_time; 213 | } 214 | 215 | double Channel::ready_time(void) 216 | { 217 | return ready_at; 218 | } 219 | 220 | -------------------------------------------------------------------------------- /ssd_config.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2009, 2010 Brendan Tauras */ 2 | 3 | /* ssd_config.cpp is part of FlashSim. */ 4 | 5 | /* FlashSim is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * any later version. */ 9 | 10 | /* FlashSim is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. */ 14 | 15 | /* You should have received a copy of the GNU General Public License 16 | * along with FlashSim. If not, see . */ 17 | 18 | /****************************************************************************/ 19 | 20 | /* Configuration loader 21 | * Brendan Tauras 2009-11-02 22 | * Matias Bjørling 2011-03 23 | * 24 | * Functions below provide basic configuration file parsing. Config file 25 | * support includes skipping blank lines, comment lines (begin with a #). 26 | * Parsed lines consist of the variable name, a space, then the value 27 | * (e.g. SSD_SIZE 4 ). Default config values (if config file is missing 28 | * an entry to set the value) are defined in the variable declarations below. 29 | * 30 | * A function is also provided for printing the current configuration. */ 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | /* using namespace ssd; */ 37 | namespace ssd { 38 | 39 | /* Define typedefs and error macros from ssd.h here instead of including 40 | * header file because we want to declare the global configuration variables 41 | * and set them in this file. ssd.h declares the global configuration 42 | * variables as extern const, which would conflict this file's definitions. 43 | * This is not the best solution, but it is easy and it works. */ 44 | 45 | /* some obvious typedefs for laziness */ 46 | typedef unsigned int uint; 47 | typedef unsigned long ulong; 48 | 49 | /* define exit codes for errors */ 50 | #define MEM_ERR -1 51 | #define FILE_ERR -2 52 | 53 | /* Simulator configuration 54 | * All configuration variables are set by reading ssd.conf and referenced with 55 | * as "extern const" in ssd.h 56 | * Configuration variables are described below and are assigned default values 57 | * in case of config file error. The values defined below are overwritten 58 | * when defined in the config file. 59 | * We do not want a class here because we want to use the configuration 60 | * variables in the same was as macros. */ 61 | 62 | /* Ram class: 63 | * delay to read from and write to the RAM for 1 page of data */ 64 | double RAM_READ_DELAY = 0.00000001; 65 | double RAM_WRITE_DELAY = 0.00000001; 66 | 67 | /* Bus class: 68 | * delay to communicate over bus 69 | * max number of connected devices allowed 70 | * number of time entries bus has to keep track of future schedule usage 71 | * value used as a flag to indicate channel is free 72 | * (use a value not used as a delay value - e.g. -1.0) 73 | * number of simultaneous communication channels - defined by SSD_SIZE */ 74 | double BUS_CTRL_DELAY = 0.000000005; 75 | double BUS_DATA_DELAY = 0.00000001; 76 | uint BUS_MAX_CONNECT = 8; 77 | uint BUS_TABLE_SIZE = 64; 78 | double BUS_CHANNEL_FREE_FLAG = -1.0; 79 | /* uint BUS_CHANNELS = 4; same as # of Packages, defined by SSD_SIZE */ 80 | 81 | /* Ssd class: 82 | * number of Packages per Ssd (size) */ 83 | uint SSD_SIZE = 4; 84 | 85 | /* Package class: 86 | * number of Dies per Package (size) */ 87 | uint PACKAGE_SIZE = 8; 88 | 89 | /* Die class: 90 | * number of Planes per Die (size) */ 91 | uint DIE_SIZE = 2; 92 | 93 | /* Plane class: 94 | * number of Blocks per Plane (size) 95 | * delay for reading from plane register 96 | * delay for writing to plane register 97 | * delay for merging is based on read, write, reg_read, reg_write 98 | * and does not need to be explicitly defined */ 99 | uint PLANE_SIZE = 64; 100 | double PLANE_REG_READ_DELAY = 0.0000000001; 101 | double PLANE_REG_WRITE_DELAY = 0.0000000001; 102 | 103 | /* Block class: 104 | * number of Pages per Block (size) 105 | * number of erases in lifetime of block 106 | * delay for erasing block */ 107 | uint BLOCK_SIZE = 16; 108 | uint BLOCK_ERASES = 1048675; 109 | double BLOCK_ERASE_DELAY = 0.001; 110 | 111 | /* Page class: 112 | * delay for Page reads 113 | * delay for Page writes */ 114 | double PAGE_READ_DELAY = 0.000001; 115 | double PAGE_WRITE_DELAY = 0.00001; 116 | 117 | /* Page data memory allocation 118 | * 119 | */ 120 | uint PAGE_SIZE = 4096; 121 | bool PAGE_ENABLE_DATA = true; 122 | 123 | /* 124 | * Memory area to support pages with data. 125 | */ 126 | void *page_data; 127 | 128 | /* 129 | * Number of blocks to reserve for mappings. e.g. map directory in BAST. 130 | */ 131 | uint MAP_DIRECTORY_SIZE = 0; 132 | 133 | /* 134 | * Implementation to use (0 -> Page, 1 -> BAST, 2 -> FAST, 3 -> DFTL, 4 -> BiModal 135 | */ 136 | uint FTL_IMPLEMENTATION = 0; 137 | 138 | /* 139 | * Limit of LOG pages (for use in BAST) 140 | */ 141 | uint BAST_LOG_BLOCK_LIMIT = 100; 142 | 143 | 144 | /* 145 | * Limit of LOG pages (for use in FAST) 146 | */ 147 | uint FAST_LOG_BLOCK_LIMIT = 4; 148 | 149 | /* 150 | * Number of pages allowed to be in DFTL Cached Mapping Table. 151 | * (Size equals CACHE_BLOCK_LIMIT * block size * page size) 152 | * 153 | */ 154 | uint CACHE_DFTL_LIMIT = 8; 155 | 156 | /* 157 | * Parallelism mode. 158 | * 0 -> Normal 159 | * 1 -> Striping 160 | * 2 -> Logical Address Space Parallelism (LASP) 161 | */ 162 | uint PARALLELISM_MODE = 0; 163 | 164 | /* Virtual block size (as a multiple of the physical block size) */ 165 | uint VIRTUAL_BLOCK_SIZE = 1; 166 | 167 | /* Virtual page size (as a multiple of the physical page size) */ 168 | uint VIRTUAL_PAGE_SIZE = 1; 169 | 170 | uint NUMBER_OF_ADDRESSABLE_BLOCKS = 0; 171 | 172 | /* RAISSDs: Number of physical SSDs */ 173 | uint RAID_NUMBER_OF_PHYSICAL_SSDS = 0; 174 | 175 | void load_entry(char *name, double value, uint line_number) { 176 | /* cheap implementation - go through all possibilities and match entry */ 177 | if (!strcmp(name, "RAM_READ_DELAY")) 178 | RAM_READ_DELAY = value; 179 | else if (!strcmp(name, "RAM_WRITE_DELAY")) 180 | RAM_WRITE_DELAY = value; 181 | else if (!strcmp(name, "BUS_CTRL_DELAY")) 182 | BUS_CTRL_DELAY = value; 183 | else if (!strcmp(name, "BUS_DATA_DELAY")) 184 | BUS_DATA_DELAY = value; 185 | else if (!strcmp(name, "BUS_MAX_CONNECT")) 186 | BUS_MAX_CONNECT = (uint) value; 187 | else if (!strcmp(name, "BUS_TABLE_SIZE")) 188 | BUS_TABLE_SIZE = (uint) value; 189 | else if (!strcmp(name, "SSD_SIZE")) 190 | SSD_SIZE = (uint) value; 191 | else if (!strcmp(name, "PACKAGE_SIZE")) 192 | PACKAGE_SIZE = (uint) value; 193 | else if (!strcmp(name, "DIE_SIZE")) 194 | DIE_SIZE = (uint) value; 195 | else if (!strcmp(name, "PLANE_SIZE")) 196 | PLANE_SIZE = (uint) value; 197 | else if (!strcmp(name, "PLANE_REG_READ_DELAY")) 198 | PLANE_REG_READ_DELAY = value; 199 | else if (!strcmp(name, "PLANE_REG_WRITE_DELAY")) 200 | PLANE_REG_WRITE_DELAY = value; 201 | else if (!strcmp(name, "BLOCK_SIZE")) 202 | BLOCK_SIZE = (uint) value; 203 | else if (!strcmp(name, "BLOCK_ERASES")) 204 | BLOCK_ERASES = (uint) value; 205 | else if (!strcmp(name, "BLOCK_ERASE_DELAY")) 206 | BLOCK_ERASE_DELAY = value; 207 | else if (!strcmp(name, "PAGE_READ_DELAY")) 208 | PAGE_READ_DELAY = value; 209 | else if (!strcmp(name, "PAGE_WRITE_DELAY")) 210 | PAGE_WRITE_DELAY = value; 211 | else if (!strcmp(name, "PAGE_SIZE")) 212 | PAGE_SIZE = value; 213 | else if (!strcmp(name, "FTL_IMPLEMENTATION")) 214 | FTL_IMPLEMENTATION = value; 215 | else if (!strcmp(name, "PAGE_ENABLE_DATA")) 216 | PAGE_ENABLE_DATA = (value == 1); 217 | else if (!strcmp(name, "MAP_DIRECTORY_SIZE")) 218 | MAP_DIRECTORY_SIZE = value; 219 | else if (!strcmp(name, "FTL_IMPLEMENTATION")) 220 | FTL_IMPLEMENTATION = value; 221 | else if (!strcmp(name, "BAST_LOG_BLOCK_LIMIT")) 222 | BAST_LOG_BLOCK_LIMIT = value; 223 | else if (!strcmp(name, "FAST_LOG_BLOCK_LIMIT")) 224 | FAST_LOG_BLOCK_LIMIT = value; 225 | else if (!strcmp(name, "CACHE_DFTL_LIMIT")) 226 | CACHE_DFTL_LIMIT = value; 227 | else if (!strcmp(name, "PARALLELISM_MODE")) 228 | PARALLELISM_MODE = value; 229 | else if (!strcmp(name, "VIRTUAL_BLOCK_SIZE")) 230 | VIRTUAL_BLOCK_SIZE = value; 231 | else if (!strcmp(name, "VIRTUAL_PAGE_SIZE")) 232 | VIRTUAL_PAGE_SIZE = value; 233 | else if (!strcmp(name, "RAID_NUMBER_OF_PHYSICAL_SSDS")) 234 | RAID_NUMBER_OF_PHYSICAL_SSDS = value; 235 | else 236 | fprintf(stderr, "Config file parsing error on line %u\n", line_number); 237 | return; 238 | } 239 | 240 | void load_config(void) { 241 | const char * const config_name = "ssd.conf"; 242 | FILE *config_file = NULL; 243 | 244 | /* update sscanf line below with max name length (%s) if changing sizes */ 245 | uint line_size = 128; 246 | char line[line_size]; 247 | uint line_number; 248 | 249 | char name[line_size]; 250 | double value; 251 | 252 | if ((config_file = fopen(config_name, "r")) == NULL) { 253 | fprintf(stderr, "Config file %s not found. Exiting.\n", config_name); 254 | exit(FILE_ERR); 255 | } 256 | 257 | for (line_number = 1; fgets(line, line_size, config_file) != NULL; line_number++) { 258 | line[line_size - 1] = '\0'; 259 | 260 | /* ignore comments and blank lines */ 261 | if (line[0] == '#' || line[0] == '\n') 262 | continue; 263 | 264 | /* read lines with entries (name value) */ 265 | if (sscanf(line, "%127s %lf", name, &value) == 2) { 266 | name[line_size - 1] = '\0'; 267 | load_entry(name, value, line_number); 268 | } else 269 | fprintf(stderr, "Config file parsing error on line %u\n", 270 | line_number); 271 | } 272 | fclose(config_file); 273 | 274 | NUMBER_OF_ADDRESSABLE_BLOCKS = (SSD_SIZE * PACKAGE_SIZE * DIE_SIZE * PLANE_SIZE) / VIRTUAL_PAGE_SIZE; 275 | 276 | return; 277 | } 278 | 279 | void print_config(FILE *stream) { 280 | if (stream == NULL) 281 | stream = stdout; 282 | fprintf(stream, "RAM_READ_DELAY: %.16lf\n", RAM_READ_DELAY); 283 | fprintf(stream, "RAM_WRITE_DELAY: %.16lf\n", RAM_WRITE_DELAY); 284 | fprintf(stream, "BUS_CTRL_DELAY: %.16lf\n", BUS_CTRL_DELAY); 285 | fprintf(stream, "BUS_DATA_DELAY: %.16lf\n", BUS_DATA_DELAY); 286 | fprintf(stream, "BUS_MAX_CONNECT: %u\n", BUS_MAX_CONNECT); 287 | fprintf(stream, "BUS_TABLE_SIZE: %u\n", BUS_TABLE_SIZE); 288 | fprintf(stream, "SSD_SIZE: %u\n", SSD_SIZE); 289 | fprintf(stream, "PACKAGE_SIZE: %u\n", PACKAGE_SIZE); 290 | fprintf(stream, "DIE_SIZE: %u\n", DIE_SIZE); 291 | fprintf(stream, "PLANE_SIZE: %u\n", PLANE_SIZE); 292 | fprintf(stream, "PLANE_REG_READ_DELAY: %.16lf\n", PLANE_REG_READ_DELAY); 293 | fprintf(stream, "PLANE_REG_WRITE_DELAY: %.16lf\n", PLANE_REG_WRITE_DELAY); 294 | fprintf(stream, "BLOCK_SIZE: %u\n", BLOCK_SIZE); 295 | fprintf(stream, "BLOCK_ERASES: %u\n", BLOCK_ERASES); 296 | fprintf(stream, "BLOCK_ERASE_DELAY: %.16lf\n", BLOCK_ERASE_DELAY); 297 | fprintf(stream, "PAGE_READ_DELAY: %.16lf\n", PAGE_READ_DELAY); 298 | fprintf(stream, "PAGE_WRITE_DELAY: %.16lf\n", PAGE_WRITE_DELAY); 299 | fprintf(stream, "PAGE_SIZE: %u\n", PAGE_SIZE); 300 | fprintf(stream, "PAGE_ENABLE_DATA: %i\n", PAGE_ENABLE_DATA); 301 | fprintf(stream, "MAP_DIRECTORY_SIZE: %i\n", MAP_DIRECTORY_SIZE); 302 | fprintf(stream, "FTL_IMPLEMENTATION: %i\n", FTL_IMPLEMENTATION); 303 | fprintf(stream, "PARALLELISM_MODE: %i\n", PARALLELISM_MODE); 304 | fprintf(stream, "RAID_NUMBER_OF_PHYSICAL_SSDS: %i\n", RAID_NUMBER_OF_PHYSICAL_SSDS); 305 | 306 | return; 307 | } 308 | 309 | } 310 | -------------------------------------------------------------------------------- /ssd_controller.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2009, 2010 Brendan Tauras */ 2 | 3 | /* ssd_controller.cpp is part of FlashSim. */ 4 | 5 | /* FlashSim is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * any later version. */ 9 | 10 | /* FlashSim is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. */ 14 | 15 | /* You should have received a copy of the GNU General Public License 16 | * along with FlashSim. If not, see . */ 17 | 18 | /****************************************************************************/ 19 | 20 | /* Controller class 21 | * 22 | * Brendan Tauras 2009-11-03 23 | * 24 | * The controller accepts read/write requests through its event_arrive method 25 | * and consults the FTL regarding what to do by calling the FTL's read/write 26 | * methods. The FTL returns an event list for the controller through its issue 27 | * method that the controller buffers in RAM and sends across the bus. The 28 | * controller's issue method passes the events from the FTL to the SSD. 29 | * 30 | * The controller also provides an interface for the FTL to collect wear 31 | * information to perform wear-leveling. 32 | */ 33 | 34 | #include 35 | #include 36 | #include 37 | #include "ssd.h" 38 | 39 | using namespace ssd; 40 | 41 | Controller::Controller(Ssd &parent): 42 | ssd(parent) 43 | { 44 | switch (FTL_IMPLEMENTATION) 45 | { 46 | case 0: 47 | ftl = new FtlImpl_Page(*this); 48 | break; 49 | case 1: 50 | ftl = new FtlImpl_Bast(*this); 51 | break; 52 | case 2: 53 | ftl = new FtlImpl_Fast(*this); 54 | break; 55 | case 3: 56 | ftl = new FtlImpl_Dftl(*this); 57 | break; 58 | case 4: 59 | ftl = new FtlImpl_BDftl(*this); 60 | break; 61 | } 62 | return; 63 | } 64 | 65 | Controller::~Controller(void) 66 | { 67 | delete ftl; 68 | return; 69 | } 70 | 71 | enum status Controller::event_arrive(Event &event) 72 | { 73 | if(event.get_event_type() == READ) 74 | return ftl->read(event); 75 | else if(event.get_event_type() == WRITE) 76 | return ftl->write(event); 77 | else if(event.get_event_type() == TRIM) 78 | return ftl->trim(event); 79 | else 80 | fprintf(stderr, "Controller: %s: Invalid event type\n", __func__); 81 | return FAILURE; 82 | } 83 | 84 | enum status Controller::issue(Event &event_list) 85 | { 86 | Event *cur; 87 | 88 | /* go through event list and issue each to the hardware 89 | * stop processing events and return failure status if any event in the 90 | * list fails */ 91 | for(cur = &event_list; cur != NULL; cur = cur -> get_next()){ 92 | if(cur -> get_size() != 1){ 93 | fprintf(stderr, "Controller: %s: Received non-single-page-sized event from FTL.\n", __func__); 94 | return FAILURE; 95 | } 96 | else if(cur -> get_event_type() == READ) 97 | { 98 | assert(cur -> get_address().valid > NONE); 99 | if(ssd.bus.lock(cur -> get_address().package, cur -> get_start_time(), BUS_CTRL_DELAY, *cur) == FAILURE 100 | || ssd.read(*cur) == FAILURE 101 | || ssd.bus.lock(cur -> get_address().package, cur -> get_start_time()+cur -> get_time_taken(), BUS_CTRL_DELAY + BUS_DATA_DELAY, *cur) == FAILURE 102 | || ssd.ram.write(*cur) == FAILURE 103 | || ssd.ram.read(*cur) == FAILURE 104 | || ssd.replace(*cur) == FAILURE) 105 | return FAILURE; 106 | } 107 | else if(cur -> get_event_type() == WRITE) 108 | { 109 | assert(cur -> get_address().valid > NONE); 110 | if(ssd.bus.lock(cur -> get_address().package, cur -> get_start_time(), BUS_CTRL_DELAY + BUS_DATA_DELAY, *cur) == FAILURE 111 | || ssd.ram.write(*cur) == FAILURE 112 | || ssd.ram.read(*cur) == FAILURE 113 | || ssd.write(*cur) == FAILURE 114 | || ssd.replace(*cur) == FAILURE) 115 | return FAILURE; 116 | } 117 | else if(cur -> get_event_type() == ERASE) 118 | { 119 | assert(cur -> get_address().valid > NONE); 120 | if(ssd.bus.lock(cur -> get_address().package, cur -> get_start_time(), BUS_CTRL_DELAY, *cur) == FAILURE 121 | || ssd.erase(*cur) == FAILURE) 122 | return FAILURE; 123 | } 124 | else if(cur -> get_event_type() == MERGE) 125 | { 126 | assert(cur -> get_address().valid > NONE); 127 | assert(cur -> get_merge_address().valid > NONE); 128 | if(ssd.bus.lock(cur -> get_address().package, cur -> get_start_time(), BUS_CTRL_DELAY, *cur) == FAILURE 129 | || ssd.merge(*cur) == FAILURE) 130 | return FAILURE; 131 | } 132 | else if(cur -> get_event_type() == TRIM) 133 | { 134 | return SUCCESS; 135 | } 136 | else 137 | { 138 | fprintf(stderr, "Controller: %s: Invalid event type\n", __func__); 139 | return FAILURE; 140 | } 141 | } 142 | return SUCCESS; 143 | } 144 | 145 | void Controller::translate_address(Address &address) 146 | { 147 | if (PARALLELISM_MODE != 1) 148 | return; 149 | } 150 | 151 | ssd::ulong Controller::get_erases_remaining(const Address &address) const 152 | { 153 | assert(address.valid > NONE); 154 | return ssd.get_erases_remaining(address); 155 | } 156 | 157 | void Controller::get_least_worn(Address &address) const 158 | { 159 | assert(address.valid > NONE); 160 | return ssd.get_least_worn(address); 161 | } 162 | 163 | double Controller::get_last_erase_time(const Address &address) const 164 | { 165 | assert(address.valid > NONE); 166 | return ssd.get_last_erase_time(address); 167 | } 168 | 169 | enum page_state Controller::get_state(const Address &address) const 170 | { 171 | assert(address.valid > NONE); 172 | return (ssd.get_state(address)); 173 | } 174 | 175 | enum block_state Controller::get_block_state(const Address &address) const 176 | { 177 | assert(address.valid > NONE); 178 | return (ssd.get_block_state(address)); 179 | } 180 | 181 | void Controller::get_free_page(Address &address) const 182 | { 183 | assert(address.valid > NONE); 184 | ssd.get_free_page(address); 185 | return; 186 | } 187 | 188 | ssd::uint Controller::get_num_free(const Address &address) const 189 | { 190 | assert(address.valid > NONE); 191 | return ssd.get_num_free(address); 192 | } 193 | 194 | ssd::uint Controller::get_num_valid(const Address &address) const 195 | { 196 | assert(address.valid > NONE); 197 | return ssd.get_num_valid(address); 198 | } 199 | 200 | ssd::uint Controller::get_num_invalid(const Address &address) const 201 | { 202 | assert(address.valid > NONE); 203 | return ssd.get_num_invalid(address); 204 | } 205 | 206 | Block *Controller::get_block_pointer(const Address & address) 207 | { 208 | return ssd.get_block_pointer(address); 209 | } 210 | 211 | const FtlParent &Controller::get_ftl(void) const 212 | { 213 | return (*ftl); 214 | } 215 | 216 | void Controller::print_ftl_statistics() 217 | { 218 | ftl->print_ftl_statistics(); 219 | } 220 | -------------------------------------------------------------------------------- /ssd_die.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2009, 2010 Brendan Tauras */ 2 | 3 | /* ssd_die.cpp is part of FlashSim. */ 4 | 5 | /* FlashSim is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * any later version. */ 9 | 10 | /* FlashSim is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. */ 14 | 15 | /* You should have received a copy of the GNU General Public License 16 | * along with FlashSim. If not, see . */ 17 | 18 | /****************************************************************************/ 19 | 20 | /* Die class 21 | * Brendan Tauras 2009-11-03 22 | * 23 | * The die is the data storage hardware unit that contains planes and is a flash 24 | * chip. Dies maintain wear statistics for the FTL. */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include "ssd.h" 30 | 31 | using namespace ssd; 32 | 33 | Die::Die(const Package &parent, Channel &channel, uint die_size, long physical_address): 34 | size(die_size), 35 | 36 | /* use a const pointer (Plane * const data) to use as an array 37 | * but like a reference, we cannot reseat the pointer */ 38 | data((Plane *) malloc(size * sizeof(Plane))), 39 | parent(parent), 40 | channel(channel), 41 | 42 | /* assume all Planes are same so first one can start as least worn */ 43 | least_worn(0), 44 | 45 | /* set erases remaining to BLOCK_ERASES to match Block constructor args 46 | * in Plane class 47 | * this is the cheap implementation but can change to pass through classes */ 48 | erases_remaining(BLOCK_ERASES), 49 | 50 | /* assume hardware created at time 0 and had an implied free erasure */ 51 | last_erase_time(0.0) 52 | { 53 | uint i; 54 | 55 | if(channel.connect() == FAILURE) 56 | fprintf(stderr, "Die error: %s: constructor unable to connect to Bus Channel\n", __func__); 57 | 58 | /* new cannot initialize an array with constructor args so 59 | * malloc the array 60 | * then use placement new to call the constructor for each element 61 | * chose an array over container class so we don't have to rely on anything 62 | * i.e. STL's std::vector */ 63 | /* array allocated in initializer list: 64 | * data = (Plane *) malloc(size * sizeof(Plane)); */ 65 | if(data == NULL){ 66 | fprintf(stderr, "Die error: %s: constructor unable to allocate Plane data\n", __func__); 67 | exit(MEM_ERR); 68 | } 69 | 70 | 71 | 72 | for(i = 0; i < size; i++) 73 | (void) new (&data[i]) Plane(*this, PLANE_SIZE, PLANE_REG_READ_DELAY, PLANE_REG_WRITE_DELAY, physical_address+(PLANE_SIZE*BLOCK_SIZE*i)); 74 | 75 | return; 76 | } 77 | 78 | Die::~Die(void) 79 | { 80 | assert(data != NULL); 81 | uint i; 82 | /* call destructor for each Block array element 83 | * since we used malloc and placement new */ 84 | for(i = 0; i < size; i++) 85 | data[i].~Plane(); 86 | free(data); 87 | (void) channel.disconnect(); 88 | return; 89 | } 90 | 91 | enum status Die::read(Event &event) 92 | { 93 | assert(data != NULL); 94 | assert(event.get_address().plane < size && event.get_address().valid > DIE); 95 | return data[event.get_address().plane].read(event); 96 | } 97 | 98 | enum status Die::write(Event &event) 99 | { 100 | assert(data != NULL); 101 | assert(event.get_address().plane < size && event.get_address().valid > DIE); 102 | return data[event.get_address().plane].write(event); 103 | } 104 | 105 | enum status Die::replace(Event &event) 106 | { 107 | assert(data != NULL); 108 | assert(event.get_address().plane < size); 109 | return data[event.get_replace_address().plane].replace(event); 110 | } 111 | 112 | /* if no errors 113 | * updates last_erase_time if later time 114 | * updates erases_remaining if smaller value 115 | * returns 1 for success, 0 for failure */ 116 | enum status Die::erase(Event &event) 117 | { 118 | assert(data != NULL); 119 | assert(event.get_address().plane < size && event.get_address().valid > DIE); 120 | enum status status = data[event.get_address().plane].erase(event); 121 | 122 | /* update values if no errors */ 123 | if(status == SUCCESS) 124 | update_wear_stats(event.get_address()); 125 | return status; 126 | } 127 | 128 | /* TODO: move Plane::_merge() to Die and make generic to handle merge across 129 | * both cases: 2 separate planes or within 1 plane */ 130 | enum status Die::merge(Event &event) 131 | { 132 | assert(data != NULL); 133 | assert(event.get_address().plane < size && event.get_address().valid > DIE && event.get_merge_address().plane < size && event.get_merge_address().valid > DIE); 134 | if(event.get_address().plane != event.get_merge_address().plane) 135 | return _merge(event); 136 | else return data[event.get_address().plane]._merge(event); 137 | } 138 | 139 | /* TODO: update stub as per Die::merge() comment above 140 | * to support Die-level merge operations */ 141 | enum status Die::_merge(Event &event) 142 | { 143 | assert(data != NULL); 144 | assert(event.get_address().plane < size && event.get_address().valid > DIE && event.get_merge_address().plane < size && event.get_merge_address().valid > DIE); 145 | assert(event.get_address().plane != event.get_merge_address().plane); 146 | return SUCCESS; 147 | } 148 | 149 | const Package &Die::get_parent(void) const 150 | { 151 | return parent; 152 | } 153 | 154 | /* if given a valid Block address, call the Block's method 155 | * else return local value */ 156 | double Die::get_last_erase_time(const Address &address) const 157 | { 158 | assert(data != NULL); 159 | if(address.valid > DIE && address.plane < size) 160 | return data[address.plane].get_last_erase_time(address); 161 | else 162 | return last_erase_time; 163 | } 164 | 165 | /* if given a valid Plane address, call the Plane's method 166 | * else return local value */ 167 | ssd::ulong Die::get_erases_remaining(const Address &address) const 168 | { 169 | assert(data != NULL); 170 | if(address.valid > DIE && address.plane < size) 171 | return data[address.plane].get_erases_remaining(address); 172 | else 173 | return erases_remaining; 174 | } 175 | 176 | 177 | 178 | /* Plane with the most erases remaining is the least worn */ 179 | void Die::update_wear_stats(const Address &address) 180 | { 181 | assert(data != NULL); 182 | uint i; 183 | uint max_index = 0; 184 | ulong max = data[0].get_erases_remaining(address); 185 | for(i = 1; i < size; i++) 186 | if(data[i].get_erases_remaining(address) > max) 187 | max_index = i; 188 | least_worn = max_index; 189 | erases_remaining = max; 190 | last_erase_time = data[max_index].get_last_erase_time(address); 191 | return; 192 | } 193 | 194 | /* update given address -> die to least worn die */ 195 | void Die::get_least_worn(Address &address) const 196 | { 197 | assert(data != NULL && least_worn < size); 198 | address.plane = least_worn; 199 | address.valid = PLANE; 200 | data[least_worn].get_least_worn(address); 201 | return; 202 | } 203 | 204 | enum page_state Die::get_state(const Address &address) const 205 | { 206 | assert(data != NULL && address.plane < size && address.valid >= DIE); 207 | return data[address.plane].get_state(address); 208 | } 209 | 210 | enum block_state Die::get_block_state(const Address &address) const 211 | { 212 | assert(data != NULL && address.plane < size && address.valid >= DIE); 213 | return data[address.plane].get_block_state(address); 214 | } 215 | 216 | void Die::get_free_page(Address &address) const 217 | { 218 | assert(address.plane < size && address.valid >= PLANE); 219 | data[address.plane].get_free_page(address); 220 | return; 221 | } 222 | 223 | ssd::uint Die::get_num_free(const Address &address) const 224 | { 225 | assert(address.valid >= PLANE); 226 | return data[address.plane].get_num_free(address); 227 | } 228 | 229 | ssd::uint Die::get_num_valid(const Address &address) const 230 | { 231 | assert(address.valid >= PLANE); 232 | return data[address.plane].get_num_valid(address); 233 | } 234 | 235 | ssd::uint Die::get_num_invalid(const Address & address) const 236 | { 237 | assert(address.valid >= PLANE); 238 | return data[address.plane].get_num_invalid(address); 239 | } 240 | 241 | Block *Die::get_block_pointer(const Address & address) 242 | { 243 | assert(address.valid >= PLANE); 244 | return data[address.plane].get_block_pointer(address); 245 | } 246 | -------------------------------------------------------------------------------- /ssd_event.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2009, 2010 Brendan Tauras */ 2 | 3 | /* ssd_event.cpp is part of FlashSim. */ 4 | 5 | /* FlashSim is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * any later version. */ 9 | 10 | /* FlashSim is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. */ 14 | 15 | /* You should have received a copy of the GNU General Public License 16 | * along with FlashSim. If not, see . */ 17 | 18 | /****************************************************************************/ 19 | 20 | /* Event class 21 | * Brendan Tauras 2010-07-16 22 | * 23 | * Class to manage I/O requests as events for the SSD. It was designed to keep 24 | * track of an I/O request by storing its type, addressing, and timing. The 25 | * SSD class creates an instance for each I/O request it receives. 26 | */ 27 | 28 | #include 29 | #include 30 | #include "ssd.h" 31 | 32 | using namespace ssd; 33 | 34 | /* see "enum event_type" in ssd.h for details on event types */ 35 | Event::Event(enum event_type type, ulong logical_address, uint size, double start_time): 36 | start_time(start_time), 37 | time_taken(0.0), 38 | bus_wait_time(0.0), 39 | type(type), 40 | logical_address(logical_address), 41 | size(size), 42 | payload(NULL), 43 | next(NULL), 44 | noop(false) 45 | { 46 | assert(start_time >= 0.0); 47 | return; 48 | } 49 | 50 | Event::~Event(void) 51 | { 52 | return; 53 | } 54 | 55 | /* find the last event in the list to finish and use that event's finish time 56 | * to calculate time_taken 57 | * add bus_wait_time for all events in the list to bus_wait_time 58 | * all events in the list do not need to start at the same time 59 | * bus_wait_time can potentially exceed time_taken with long event lists 60 | * because bus_wait_time is a sum while time_taken is a max 61 | * be careful to only call this method once when the metaevent is finished */ 62 | void Event::consolidate_metaevent(Event &list) 63 | { 64 | Event *cur; 65 | double max; 66 | double tmp; 67 | 68 | assert(start_time >= 0); 69 | 70 | /* find max time taken with respect to this event's start_time */ 71 | max = start_time - list.start_time + list.time_taken; 72 | for(cur = list.next; cur != NULL; cur = cur -> next) 73 | { 74 | tmp = start_time - cur -> start_time + cur -> time_taken; 75 | if(tmp > max) 76 | max = tmp; 77 | bus_wait_time += cur -> get_bus_wait_time(); 78 | } 79 | time_taken = max; 80 | 81 | assert(time_taken >= 0); 82 | assert(bus_wait_time >= 0); 83 | return; 84 | } 85 | 86 | ssd::ulong Event::get_logical_address(void) const 87 | { 88 | return logical_address; 89 | } 90 | 91 | const Address &Event::get_address(void) const 92 | { 93 | return address; 94 | } 95 | 96 | const Address &Event::get_merge_address(void) const 97 | { 98 | return merge_address; 99 | } 100 | 101 | const Address &Event::get_log_address(void) const 102 | { 103 | return log_address; 104 | } 105 | 106 | const Address &Event::get_replace_address(void) const 107 | { 108 | return replace_address; 109 | } 110 | 111 | void Event::set_log_address(const Address &address) 112 | { 113 | log_address = address; 114 | } 115 | 116 | ssd::uint Event::get_size(void) const 117 | { 118 | return size; 119 | } 120 | 121 | enum event_type Event::get_event_type(void) const 122 | { 123 | return type; 124 | } 125 | 126 | void Event::set_event_type(const enum event_type &type) 127 | { 128 | this->type = type; 129 | } 130 | 131 | double Event::get_start_time(void) const 132 | { 133 | assert(start_time >= 0.0); 134 | return start_time; 135 | } 136 | 137 | double Event::get_time_taken(void) const 138 | { 139 | 140 | assert(time_taken >= 0.0); 141 | return time_taken; 142 | } 143 | 144 | double Event::get_bus_wait_time(void) const 145 | { 146 | assert(bus_wait_time >= 0.0); 147 | return bus_wait_time; 148 | } 149 | 150 | bool Event::get_noop(void) const 151 | { 152 | return noop; 153 | } 154 | 155 | Event *Event::get_next(void) const 156 | { 157 | return next; 158 | } 159 | 160 | void Event::set_payload(void *payload) 161 | { 162 | this->payload = payload; 163 | } 164 | 165 | void *Event::get_payload(void) const 166 | { 167 | return payload; 168 | } 169 | 170 | void Event::set_address(const Address &address) 171 | { 172 | this -> address = address; 173 | return; 174 | } 175 | 176 | void Event::set_merge_address(const Address &address) 177 | { 178 | merge_address = address; 179 | return; 180 | } 181 | 182 | void Event::set_replace_address(const Address &address) 183 | { 184 | replace_address = address; 185 | } 186 | 187 | void Event::set_noop(bool value) 188 | { 189 | noop = value; 190 | } 191 | 192 | void Event::set_next(Event &next) 193 | { 194 | this -> next = &next; 195 | return; 196 | } 197 | 198 | double Event::incr_bus_wait_time(double time_incr) 199 | { 200 | if(time_incr > 0.0) 201 | bus_wait_time += time_incr; 202 | return bus_wait_time; 203 | } 204 | 205 | double Event::incr_time_taken(double time_incr) 206 | { 207 | if(time_incr > 0.0) 208 | time_taken += time_incr; 209 | return time_taken; 210 | } 211 | 212 | void Event::print(FILE *stream) 213 | { 214 | if(type == READ) 215 | fprintf(stream, "Read "); 216 | else if(type == WRITE) 217 | fprintf(stream, "Write"); 218 | else if(type == ERASE) 219 | fprintf(stream, "Erase"); 220 | else if(type == MERGE) 221 | fprintf(stream, "Merge"); 222 | else 223 | fprintf(stream, "Unknown event type: "); 224 | address.print(stream); 225 | if(type == MERGE) 226 | merge_address.print(stream); 227 | fprintf(stream, " Time[%f, %f) Bus_wait: %f\n", start_time, start_time + time_taken, bus_wait_time); 228 | return; 229 | } 230 | 231 | #if 0 232 | /* may be useful for further integration with DiskSim */ 233 | 234 | /* caution: copies pointers from rhs */ 235 | ioreq_event &Event::operator= (const ioreq_event &rhs) 236 | { 237 | assert(&rhs != NULL); 238 | if((const ioreq_event *) &rhs == (const ioreq_event *) &(this -> ioreq)) 239 | return *(this -> ioreq); 240 | ioreq -> time = rhs.time; 241 | ioreq -> type = rhs.type; 242 | ioreq -> next = rhs.next; 243 | ioreq -> prev = rhs.prev; 244 | ioreq -> bcount = rhs.bcount; 245 | ioreq -> blkno = rhs.blkno; 246 | ioreq -> flags = rhs.flags; 247 | ioreq -> busno = rhs.busno; 248 | ioreq -> slotno = rhs.slotno; 249 | ioreq -> devno = rhs.devno; 250 | ioreq -> opid = rhs.opid; 251 | ioreq -> buf = rhs.buf; 252 | ioreq -> cause = rhs.cause; 253 | ioreq -> tempint1 = rhs.tempint1; 254 | ioreq -> tempint2 = rhs.tempint2; 255 | ioreq -> tempptr1 = rhs.tempptr1; 256 | ioreq -> tempptr2 = rhs.tempptr2; 257 | ioreq -> mems_sled = rhs.mems_sled; 258 | ioreq -> mems_reqinfo = rhs.mems_reqinfo; 259 | ioreq -> start_time = rhs.start_time; 260 | ioreq -> batchno = rhs.batchno; 261 | ioreq -> batch_complete = rhs.batch_complete; 262 | ioreq -> batch_size = rhs.batch_size; 263 | ioreq -> batch_next = rhs.batch_next; 264 | ioreq -> batch_prev = rhs.batch_prev; 265 | return *ioreq; 266 | } 267 | #endif 268 | -------------------------------------------------------------------------------- /ssd_ftl.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2009, 2010 Brendan Tauras */ 2 | 3 | /* ssd_ftl.cpp is part of FlashSim. */ 4 | 5 | /* FlashSim is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * any later version. */ 9 | 10 | /* FlashSim is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. */ 14 | 15 | /* You should have received a copy of the GNU General Public License 16 | * along with FlashSim. If not, see . */ 17 | 18 | /****************************************************************************/ 19 | 20 | /* Ftl class 21 | * Brendan Tauras 2009-11-04 22 | * 23 | * This class is a stub class for the user to use as a template for implementing 24 | * his/her FTL scheme. A few functions to gather information from lower-level 25 | * hardware are added to assist writing a FTL scheme. The Ftl class should 26 | * rely on the Garbage_collector and Wear_leveler classes for modularity and 27 | * simplicity. */ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "ssd.h" 34 | 35 | using namespace ssd; 36 | 37 | Ftl::Ftl(Controller &controller): 38 | controller(controller), 39 | garbage(*this), 40 | wear(*this) 41 | { 42 | currentPage = 0; 43 | 44 | uint numCells = SSD_SIZE * PACKAGE_SIZE * DIE_SIZE * PLANE_SIZE * BLOCK_SIZE; 45 | map = new long[numCells]; 46 | for (int i=0;i. */ 17 | 18 | /****************************************************************************/ 19 | 20 | /* 21 | * Implements parent interface for all FTL implementations to use. 22 | */ 23 | 24 | #include "ssd.h" 25 | 26 | using namespace ssd; 27 | 28 | // Initialization of the block layer. 29 | Block_manager *Block_manager::inst = NULL; 30 | 31 | FtlParent::FtlParent(Controller &controller) : controller(controller) 32 | { 33 | Block_manager::instance_initialize(this); 34 | 35 | printf("Number of addressable blocks: %u\n", NUMBER_OF_ADDRESSABLE_BLOCKS); 36 | 37 | } 38 | 39 | 40 | ssd::ulong FtlParent::get_erases_remaining(const Address &address) const 41 | { 42 | return controller.get_erases_remaining(address); 43 | } 44 | 45 | void FtlParent::get_least_worn(Address &address) const 46 | { 47 | controller.get_least_worn(address); 48 | return; 49 | } 50 | 51 | enum page_state FtlParent::get_state(const Address &address) const 52 | { 53 | return controller.get_state(address); 54 | } 55 | 56 | enum block_state FtlParent::get_block_state(const Address &address) const 57 | { 58 | return controller.get_block_state(address); 59 | } 60 | 61 | Block *FtlParent::get_block_pointer(const Address &address) 62 | { 63 | return controller.get_block_pointer(address); 64 | } 65 | 66 | void FtlParent::cleanup_block(Event &event, Block *block) 67 | { 68 | assert(false); 69 | return; 70 | } 71 | 72 | void FtlParent::print_ftl_statistics() 73 | { 74 | return; 75 | } 76 | -------------------------------------------------------------------------------- /ssd_gc.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2009, 2010 Brendan Tauras */ 2 | 3 | /* ssd_gc.cpp is part of FlashSim. */ 4 | 5 | /* FlashSim is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * any later version. */ 9 | 10 | /* FlashSim is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. */ 14 | 15 | /* You should have received a copy of the GNU General Public License 16 | * along with FlashSim. If not, see . */ 17 | 18 | /****************************************************************************/ 19 | 20 | /* Garbage_collector class 21 | * Brendan Tauras 2009-11-04 22 | * 23 | * This class is a stub class for the user to use as a template for implementing 24 | * his/her garbage collector scheme. The garbage collector class was added to 25 | * simplify and modularize the garbage collection in FTL schemes. */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include "ssd.h" 31 | 32 | using namespace ssd; 33 | 34 | Garbage_collector::Garbage_collector(FtlParent &ftl) 35 | { 36 | return; 37 | } 38 | 39 | Garbage_collector::~Garbage_collector(void) 40 | { 41 | return; 42 | } 43 | 44 | void Garbage_collector::clean(Address &address) 45 | { 46 | 47 | } 48 | -------------------------------------------------------------------------------- /ssd_package.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2009, 2010 Brendan Tauras */ 2 | 3 | /* ssd_package.cpp is part of FlashSim. */ 4 | 5 | /* FlashSim is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * any later version. */ 9 | 10 | /* FlashSim is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. */ 14 | 15 | /* You should have received a copy of the GNU General Public License 16 | * along with FlashSim. If not, see . */ 17 | 18 | /****************************************************************************/ 19 | 20 | /* Package class 21 | * Brendan Tauras 2009-11-03 22 | * 23 | * The package is the highest level data storage hardware unit. While the 24 | * package is a virtual component, events are passed through the package for 25 | * organizational reasons, including helping to simplify maintaining wear 26 | * statistics for the FTL. */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include "ssd.h" 32 | 33 | using namespace ssd; 34 | 35 | Package::Package(const ssd::Ssd &parent, Channel &channel, uint package_size, long physical_address): 36 | size(package_size), 37 | 38 | /* use a const pointer (Die * const data) to use as an array 39 | * but like a reference, we cannot reseat the pointer */ 40 | data((Die *) malloc(package_size * sizeof(Die))), 41 | parent(parent), 42 | 43 | /* assume all Dies are same so first one can start as least worn */ 44 | least_worn(0), 45 | 46 | /* set erases remaining to BLOCK_ERASES to match Block constructor args 47 | * in Plane class 48 | * this is the cheap implementation but can change to pass through classes */ 49 | erases_remaining(BLOCK_ERASES), 50 | 51 | /* assume hardware created at time 0 and had an implied free erasure */ 52 | last_erase_time(0.0) 53 | { 54 | uint i; 55 | 56 | /* new cannot initialize an array with constructor args so 57 | * malloc the array 58 | * then use placement new to call the constructor for each element 59 | * chose an array over container class so we don't have to rely on anything 60 | * i.e. STL's std::vector */ 61 | /* array allocated in initializer list: 62 | * data = (Die *) malloc(size * sizeof(Die)); */ 63 | if(data == NULL){ 64 | fprintf(stderr, "Package error: %s: constructor unable to allocate Die data\n", __func__); 65 | exit(MEM_ERR); 66 | } 67 | 68 | for(i = 0; i < size; i++) 69 | (void) new (&data[i]) Die(*this, channel, DIE_SIZE, physical_address+(DIE_SIZE*PLANE_SIZE*BLOCK_SIZE*i)); 70 | 71 | return; 72 | } 73 | 74 | Package::~Package(void) 75 | { 76 | assert(data != NULL); 77 | uint i; 78 | /* call destructor for each Block array element 79 | * since we used malloc and placement new */ 80 | for(i = 0; i < size; i++) 81 | data[i].~Die(); 82 | free(data); 83 | return; 84 | } 85 | 86 | enum status Package::read(Event &event) 87 | { 88 | assert(data != NULL && event.get_address().die < size && event.get_address().valid > PACKAGE); 89 | return data[event.get_address().die].read(event); 90 | } 91 | 92 | enum status Package::write(Event &event) 93 | { 94 | assert(data != NULL && event.get_address().die < size && event.get_address().valid > PACKAGE); 95 | return data[event.get_address().die].write(event); 96 | } 97 | 98 | enum status Package::replace(Event &event) 99 | { 100 | assert(data != NULL); 101 | return data[event.get_replace_address().die].replace(event); 102 | } 103 | 104 | enum status Package::erase(Event &event) 105 | { 106 | assert(data != NULL && event.get_address().die < size && event.get_address().valid > PACKAGE); 107 | enum status status = data[event.get_address().die].erase(event); 108 | if(status == SUCCESS) 109 | update_wear_stats(event.get_address()); 110 | return status; 111 | } 112 | 113 | enum status Package::merge(Event &event) 114 | { 115 | assert(data != NULL && event.get_address().die < size && event.get_address().valid > PACKAGE); 116 | return data[event.get_address().die].merge(event); 117 | } 118 | 119 | const Ssd &Package::get_parent(void) const 120 | { 121 | return parent; 122 | } 123 | 124 | /* if given a valid Block address, call the Block's method 125 | * else return local value */ 126 | double Package::get_last_erase_time(const Address &address) const 127 | { 128 | assert(data != NULL); 129 | if(address.valid > PACKAGE && address.die < size) 130 | return data[address.die].get_last_erase_time(address); 131 | else 132 | return last_erase_time; 133 | } 134 | 135 | /* if given a valid Die address, call the Die's method 136 | * else return local value */ 137 | ssd::ulong Package::get_erases_remaining(const Address &address) const 138 | { 139 | assert(data != NULL); 140 | if(address.valid > PACKAGE && address.die < size) 141 | return data[address.die].get_erases_remaining(address); 142 | else 143 | return erases_remaining; 144 | } 145 | 146 | ssd::uint ssd::Package::get_num_invalid(const Address & address) const 147 | { 148 | assert(address.valid >= DIE); 149 | return data[address.die].get_num_invalid(address); 150 | } 151 | 152 | /* Plane with the most erases remaining is the least worn */ 153 | void Package::update_wear_stats(const Address &address) 154 | { 155 | uint i; 156 | uint max_index = 0; 157 | ulong max = data[0].get_erases_remaining(address); 158 | for(i = 1; i < size; i++) 159 | if(data[i].get_erases_remaining(address) > max) 160 | max_index = i; 161 | least_worn = max_index; 162 | erases_remaining = max; 163 | last_erase_time = data[max_index].get_last_erase_time(address); 164 | return; 165 | } 166 | 167 | /* update given address -> package to least worn package */ 168 | void Package::get_least_worn(Address &address) const 169 | { 170 | assert(least_worn < size); 171 | address.die = least_worn; 172 | address.valid = DIE; 173 | data[least_worn].get_least_worn(address); 174 | return; 175 | } 176 | 177 | enum page_state Package::get_state(const Address &address) const 178 | { 179 | assert(data != NULL && address.die < size && address.valid >= PACKAGE); 180 | return data[address.die].get_state(address); 181 | } 182 | 183 | enum block_state Package::get_block_state(const Address &address) const 184 | { 185 | assert(data != NULL && address.die < size && address.valid >= PACKAGE); 186 | return data[address.die].get_block_state(address); 187 | } 188 | 189 | void Package::get_free_page(Address &address) const 190 | { 191 | assert(address.die < size && address.valid >= DIE); 192 | data[address.die].get_free_page(address); 193 | return; 194 | } 195 | ssd::uint Package::get_num_free(const Address &address) const 196 | { 197 | assert(address.valid >= DIE); 198 | return data[address.die].get_num_free(address); 199 | } 200 | 201 | ssd::uint Package::get_num_valid(const Address &address) const 202 | { 203 | assert(address.valid >= DIE); 204 | return data[address.die].get_num_valid(address); 205 | } 206 | 207 | Block *Package::get_block_pointer(const Address & address) 208 | { 209 | assert(address.valid >= DIE); 210 | return data[address.die].get_block_pointer(address); 211 | } 212 | -------------------------------------------------------------------------------- /ssd_page.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2009, 2010 Brendan Tauras */ 2 | 3 | /* ssd_page.cpp is part of FlashSim. */ 4 | 5 | /* FlashSim is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * any later version. */ 9 | 10 | /* FlashSim is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. */ 14 | 15 | /* You should have received a copy of the GNU General Public License 16 | * along with FlashSim. If not, see . */ 17 | 18 | /****************************************************************************/ 19 | 20 | /* Page class 21 | * Brendan Tauras 2009-04-06 22 | * 23 | * The page is the lowest level data storage unit that is the size unit of 24 | * requests (events). Pages maintain their state as events modify them. */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "ssd.h" 32 | 33 | namespace ssd { 34 | /* 35 | * Buffer used for accessing data pages. 36 | */ 37 | void *global_buffer; 38 | 39 | } 40 | 41 | using namespace ssd; 42 | 43 | Page::Page(const Block &parent, double read_delay, double write_delay): 44 | state(EMPTY), 45 | parent(parent), 46 | read_delay(read_delay), 47 | write_delay(write_delay) 48 | { 49 | if(read_delay < 0.0){ 50 | fprintf(stderr, "Page warning: %s: constructor received negative read delay value\n\tsetting read delay to 0.0\n", __func__); 51 | this -> read_delay = 0.0; 52 | } 53 | 54 | if(write_delay < 0.0){ 55 | fprintf(stderr, "Page warning: %s: constructor received negative write delay value\n\tsetting write delay to 0.0\n", __func__); 56 | this -> write_delay = 0.0; 57 | } 58 | return; 59 | } 60 | 61 | Page::~Page(void) 62 | { 63 | return; 64 | } 65 | 66 | enum status Page::_read(Event &event) 67 | { 68 | assert(read_delay >= 0.0); 69 | 70 | event.incr_time_taken(read_delay); 71 | 72 | if (!event.get_noop() && PAGE_ENABLE_DATA) 73 | global_buffer = (char*)page_data + event.get_address().get_linear_address() * PAGE_SIZE; 74 | 75 | return SUCCESS; 76 | } 77 | 78 | enum status Page::_write(Event &event) 79 | { 80 | assert(write_delay >= 0.0); 81 | 82 | event.incr_time_taken(write_delay); 83 | 84 | if (PAGE_ENABLE_DATA && event.get_payload() != NULL && event.get_noop() == false) 85 | { 86 | void *data = (char*)page_data + event.get_address().get_linear_address() * PAGE_SIZE; 87 | memcpy (data, event.get_payload(), PAGE_SIZE); 88 | } 89 | 90 | if (event.get_noop() == false) 91 | { 92 | assert(state == EMPTY); 93 | state = VALID; 94 | } 95 | 96 | return SUCCESS; 97 | } 98 | 99 | const Block &Page::get_parent(void) const 100 | { 101 | return parent; 102 | } 103 | 104 | enum page_state Page::get_state(void) const 105 | { 106 | return state; 107 | } 108 | 109 | void Page::set_state(enum page_state state) 110 | { 111 | this -> state = state; 112 | } 113 | -------------------------------------------------------------------------------- /ssd_plane.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2009, 2010 Brendan Tauras */ 2 | 3 | /* ssd_plane.cpp is part of FlashSim. */ 4 | 5 | /* FlashSim is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * any later version. */ 9 | 10 | /* FlashSim is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. */ 14 | 15 | /* You should have received a copy of the GNU General Public License 16 | * along with FlashSim. If not, see . */ 17 | 18 | /****************************************************************************/ 19 | 20 | /* Plane class 21 | * Brendan Tauras 2009-11-03 22 | * 23 | * The plane is the data storage hardware unit that contains blocks. 24 | * Plane-level merges are implemented in the plane. Planes maintain wear 25 | * statistics for the FTL. */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include "ssd.h" 31 | 32 | using namespace ssd; 33 | 34 | Plane::Plane(const Die &parent, uint plane_size, double reg_read_delay, double reg_write_delay, long physical_address): 35 | size(plane_size), 36 | 37 | /* use a const pointer (Block * const data) to use as an array 38 | * but like a reference, we cannot reseat the pointer */ 39 | data((Block *) malloc(size * sizeof(Block))), 40 | 41 | parent(parent), 42 | 43 | /* assume all Blocks are same so first one can start as least worn */ 44 | least_worn(0), 45 | 46 | /* set erases remaining to BLOCK_ERASES to match Block constructor args */ 47 | erases_remaining(BLOCK_ERASES), 48 | 49 | /* assume hardware created at time 0 and had an implied free erasure */ 50 | last_erase_time(0.0), 51 | 52 | free_blocks(size) 53 | { 54 | uint i; 55 | 56 | if(reg_read_delay < 0.0) 57 | { 58 | fprintf(stderr, "Plane error: %s: constructor received negative register read delay value\n\tsetting register read delay to 0.0\n", __func__); 59 | reg_read_delay = 0.0; 60 | } 61 | else 62 | this -> reg_read_delay = reg_read_delay; 63 | 64 | if(reg_write_delay < 0.0) 65 | { 66 | fprintf(stderr, "Plane error: %s: constructor received negative register write delay value\n\tsetting register write delay to 0.0\n", __func__); 67 | reg_write_delay = 0.0; 68 | } 69 | else 70 | this -> reg_write_delay = reg_write_delay; 71 | 72 | /* next page only uses the block, page, and valid fields of the address 73 | * object so we can ignore setting the other fields 74 | * plane does not know about higher-level hardware organization, so we cannot 75 | * set the other fields anyway */ 76 | next_page.block = 0; 77 | next_page.page = 0; 78 | next_page.valid = PAGE; 79 | 80 | /* new cannot initialize an array with constructor args so 81 | * malloc the array 82 | * then use placement new to call the constructor for each element 83 | * chose an array over container class so we don't have to rely on anything 84 | * i.e. STL's std::vector */ 85 | /* array allocated in initializer list: 86 | * data = (Block *) malloc(size * sizeof(Block)); */ 87 | if(data == NULL){ 88 | fprintf(stderr, "Plane error: %s: constructor unable to allocate Block data\n", __func__); 89 | exit(MEM_ERR); 90 | } 91 | 92 | for(i = 0; i < size; i++) 93 | { 94 | (void) new (&data[i]) Block(*this, BLOCK_SIZE, BLOCK_ERASES, BLOCK_ERASE_DELAY,physical_address+(i*BLOCK_SIZE)); 95 | } 96 | 97 | 98 | return; 99 | } 100 | 101 | Plane::~Plane(void) 102 | { 103 | assert(data != NULL); 104 | uint i; 105 | /* call destructor for each Block array element 106 | * since we used malloc and placement new */ 107 | for(i = 0; i < size; i++) 108 | data[i].~Block(); 109 | free(data); 110 | return; 111 | } 112 | 113 | enum status Plane::read(Event &event) 114 | { 115 | assert(event.get_address().block < size && event.get_address().valid > PLANE); 116 | return data[event.get_address().block].read(event); 117 | } 118 | 119 | enum status Plane::write(Event &event) 120 | { 121 | assert(event.get_address().block < size && event.get_address().valid > PLANE && next_page.valid >= BLOCK); 122 | 123 | enum block_state prev = data[event.get_address().block].get_state(); 124 | 125 | status s = data[event.get_address().block].write(event); 126 | 127 | if(event.get_address().block == next_page.block) 128 | /* if all blocks in the plane are full and this function fails, 129 | * the next_page address valid field will be set to PLANE */ 130 | (void) get_next_page(); 131 | 132 | if(prev == FREE && data[event.get_address().block].get_state() != FREE) 133 | free_blocks--; 134 | 135 | return s; 136 | } 137 | 138 | enum status Plane::replace(Event &event) 139 | { 140 | assert(event.get_address().block < size); 141 | return data[event.get_replace_address().block].replace(event); 142 | } 143 | 144 | 145 | /* if no errors 146 | * updates last_erase_time if later time 147 | * updates erases_remaining if smaller value 148 | * returns 1 for success, 0 for failure */ 149 | enum status Plane::erase(Event &event) 150 | { 151 | assert(event.get_address().block < size && event.get_address().valid > PLANE); 152 | enum status status = data[event.get_address().block]._erase(event); 153 | 154 | /* update values if no errors */ 155 | if(status == 1) 156 | { 157 | update_wear_stats(); 158 | free_blocks++; 159 | 160 | /* set next free page if plane was completely full */ 161 | if(next_page.valid < PAGE) 162 | (void) get_next_page(); 163 | } 164 | return status; 165 | } 166 | 167 | /* handle everything for a merge operation 168 | * address.block and address_merge.block must be valid 169 | * move event::address valid pages to event::address_merge empty pages 170 | * creates own events for resulting read/write operations 171 | * supports blocks that have different sizes */ 172 | enum status Plane::_merge(Event &event) 173 | { 174 | assert(event.get_address().block < size && event.get_address().valid > PLANE); 175 | assert(reg_read_delay >= 0.0 && reg_write_delay >= 0.0); 176 | uint i; 177 | uint merge_count = 0; 178 | uint merge_avail = 0; 179 | uint num_merged = 0; 180 | double total_delay = 0; 181 | 182 | /* get and check address validity and size of blocks involved in the merge */ 183 | const Address &address = event.get_address(); 184 | const Address &merge_address = event.get_merge_address(); 185 | assert(address.compare(merge_address) >= BLOCK); 186 | assert(address.block < size && merge_address.block < size); 187 | uint block_size = data[address.block].get_size(); 188 | uint merge_block_size = data[merge_address.block].get_size(); 189 | 190 | /* how many pages must be moved */ 191 | for(i = 0; i < block_size; i++) 192 | if(data[address.block].get_state(i) == VALID) 193 | merge_count++; 194 | 195 | /* how many pages are available */ 196 | for(i = 0; i < merge_block_size; i++) 197 | if(data[merge_address.block].get_state(i) == EMPTY) 198 | merge_avail++; 199 | 200 | /* fail if not enough space to do the merge */ 201 | if(merge_count > merge_avail) 202 | { 203 | fprintf(stderr, "Plane error: %s: Not enough space to merge block %d into block %d\n", __func__, address.block, merge_address.block); 204 | return FAILURE; 205 | } 206 | 207 | /* create event classes to handle read and write events for the merge */ 208 | Address read(address); 209 | Address write(merge_address); 210 | read.page = 0; 211 | read.valid = PAGE; 212 | write.page = 0; 213 | write.valid = PAGE; 214 | Event read_event(READ, 0, 1, event.get_start_time()); 215 | Event write_event(WRITE, 0, 1, event.get_start_time()); 216 | read_event.set_address(read); 217 | write_event.set_address(write); 218 | 219 | /* calculate merge delay and add to event time 220 | * use i as an error counter */ 221 | for(i = 0; num_merged < merge_count && read.page < block_size; read.page++) 222 | { 223 | /* find next page to read from */ 224 | if(data[read.block].get_state(read.page) == VALID) 225 | { 226 | /* read from page and set status to invalid */ 227 | if(data[read.block].read(read_event) == 0) 228 | { 229 | fprintf(stderr, "Plane error: %s: Read for merge block %d into %d failed\n", __func__, read.block, write.block); 230 | i++; 231 | } 232 | data[read.block].invalidate_page(read.page); 233 | 234 | /* get time taken for read and plane register write 235 | * read event time will accumulate and be added at end */ 236 | total_delay += reg_write_delay; 237 | 238 | /* keep advancing from last page written to */ 239 | for(; write.page < merge_block_size; write.page++) 240 | { 241 | /* find next page to write to */ 242 | if(data[write.block].get_state(write.page) == EMPTY) 243 | { 244 | /* write to page (page::_write() sets status to valid) */ 245 | if(data[merge_address.block].write(write_event) == 0) 246 | { 247 | fprintf(stderr, "Plane error: %s: Write for merge block %d into %d failed\n", __func__, address.block, merge_address.block); 248 | i++; 249 | } 250 | 251 | /* get time taken for plane register read 252 | * write event time will accumulate and be added at end */ 253 | total_delay += reg_read_delay; 254 | num_merged++; 255 | break; 256 | } 257 | } 258 | } 259 | } 260 | total_delay += read_event.get_time_taken() + write_event.get_time_taken(); 261 | event.incr_time_taken(total_delay); 262 | 263 | /* update next_page for the get_free_page method if we used the page */ 264 | if(next_page.valid < PAGE) 265 | (void) get_next_page(); 266 | 267 | if(i == 0) 268 | return SUCCESS; 269 | else 270 | { 271 | fprintf(stderr, "Plane error: %s: %u failures during merge operation\n", __func__, i); 272 | return FAILURE; 273 | } 274 | } 275 | 276 | ssd::uint Plane::get_size(void) const 277 | { 278 | return size; 279 | } 280 | 281 | const Die &Plane::get_parent(void) const 282 | { 283 | return parent; 284 | } 285 | 286 | /* if given a valid Block address, call the Block's method 287 | * else return local value */ 288 | double Plane::get_last_erase_time(const Address &address) const 289 | { 290 | assert(data != NULL); 291 | if(address.valid > PLANE && address.block < size) 292 | return data[address.block].get_last_erase_time(); 293 | else 294 | return last_erase_time; 295 | } 296 | 297 | /* if given a valid Block address, call the Block's method 298 | * else return local value */ 299 | ssd::ulong Plane::get_erases_remaining(const Address &address) const 300 | { 301 | assert(data != NULL); 302 | if(address.valid > PLANE && address.block < size) 303 | return data[address.block].get_erases_remaining(); 304 | else 305 | return erases_remaining; 306 | } 307 | 308 | /* Block with the most erases remaining is the least worn */ 309 | void Plane::update_wear_stats(void) 310 | { 311 | uint i; 312 | uint max_index = 0; 313 | ulong max = data[0].get_erases_remaining(); 314 | for(i = 1; i < size; i++) 315 | if(data[i].get_erases_remaining() > max) 316 | max_index = i; 317 | least_worn = max_index; 318 | erases_remaining = max; 319 | last_erase_time = data[max_index].get_last_erase_time(); 320 | return; 321 | } 322 | 323 | /* update given address.block to least worn block */ 324 | void Plane::get_least_worn(Address &address) const 325 | { 326 | assert(least_worn < size); 327 | address.block = least_worn; 328 | address.valid = BLOCK; 329 | return; 330 | } 331 | 332 | enum page_state Plane::get_state(const Address &address) const 333 | { 334 | assert(data != NULL && address.block < size && address.valid >= PLANE); 335 | return data[address.block].get_state(address); 336 | } 337 | 338 | enum block_state Plane::get_block_state(const Address &address) const 339 | { 340 | assert(data != NULL && address.block < size && address.valid >= PLANE); 341 | return data[address.block].get_state(); 342 | } 343 | 344 | /* update address to next free page in plane 345 | * error condition will result in (address.valid < PAGE) */ 346 | void Plane::get_free_page(Address &address) const 347 | { 348 | assert(data[address.block].get_pages_valid() < BLOCK_SIZE); 349 | 350 | address.page = data[address.block].get_pages_valid(); 351 | address.valid = PAGE; 352 | address.set_linear_address(address.get_linear_address()+ address.page - (address.get_linear_address()%BLOCK_SIZE)); 353 | return; 354 | } 355 | 356 | /* internal method to keep track of the next usable (free or active) page in 357 | * this plane 358 | * method is called by write and erase methods and calls Block::get_next_page() 359 | * such that the get_free_page method can run in constant time */ 360 | enum status Plane::get_next_page(void) 361 | { 362 | return SUCCESS; 363 | 364 | uint i; 365 | next_page.valid = PLANE; 366 | 367 | for(i = 0; i < size; i++) 368 | { 369 | if(data[i].get_state() != INACTIVE) 370 | { 371 | next_page.valid = BLOCK; 372 | if(data[i].get_next_page(next_page) == SUCCESS) 373 | { 374 | next_page.block = i; 375 | return SUCCESS; 376 | } 377 | } 378 | } 379 | return FAILURE; 380 | } 381 | 382 | /* free_blocks is updated in the write and erase methods */ 383 | ssd::uint Plane::get_num_free(const Address &address) const 384 | { 385 | assert(address.valid >= PLANE); 386 | return free_blocks; 387 | } 388 | 389 | ssd::uint Plane::get_num_valid(const Address &address) const 390 | { 391 | assert(address.valid >= PLANE); 392 | return data[address.block].get_pages_valid(); 393 | } 394 | 395 | ssd::uint Plane::get_num_invalid(const Address & address) const 396 | { 397 | assert(address.valid >= PLANE); 398 | return data[address.block].get_pages_invalid(); 399 | } 400 | 401 | Block *Plane::get_block_pointer(const Address & address) 402 | { 403 | assert(address.valid >= PLANE); 404 | return data[address.block].get_pointer(); 405 | } 406 | -------------------------------------------------------------------------------- /ssd_raidssd.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2012 Matias Bjørling */ 2 | 3 | /* FlashSim is free software: you can redistribute it and/or modify 4 | * it under the terms of the GNU General Public License as published by 5 | * the Free Software Foundation, either version 3 of the License, or 6 | * any later version. */ 7 | 8 | /* FlashSim is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. */ 12 | 13 | /* You should have received a copy of the GNU General Public License 14 | * along with FlashSim. If not, see . */ 15 | 16 | /****************************************************************************/ 17 | 18 | /* Ssd class 19 | * Matias Bjørling 2012-01-09 20 | * 21 | * The Raid SSD is responsible for raiding multiple SSDs together using different mapping techniques. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include "ssd.h" 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | using namespace ssd; 36 | 37 | /* use caution when editing the initialization list - initialization actually 38 | * occurs in the order of declaration in the class definition and not in the 39 | * order listed here */ 40 | RaidSsd::RaidSsd(uint ssd_size): 41 | size(ssd_size) 42 | { 43 | /* 44 | * Idea 45 | * 46 | * Create the instances of the SSDs. 47 | * 48 | * Techniques 49 | * 50 | * 1. Striping 51 | * 2. Address splitting. 52 | * 3. Complete control 53 | */ 54 | Ssds = new Ssd[RAID_NUMBER_OF_PHYSICAL_SSDS]; 55 | 56 | return; 57 | } 58 | 59 | RaidSsd::~RaidSsd(void) 60 | { 61 | return; 62 | } 63 | 64 | double RaidSsd::event_arrive(enum event_type type, ulong logical_address, uint size, double start_time) 65 | { 66 | return event_arrive(type, logical_address, size, start_time, NULL); 67 | } 68 | 69 | /* This is the function that will be called by DiskSim 70 | * Provide the event (request) type (see enum in ssd.h), 71 | * logical_address (page number), size of request in pages, and the start 72 | * time (arrive time) of the request 73 | * The SSD will process the request and return the time taken to process the 74 | * request. Remember to use the same time units as in the config file. */ 75 | double RaidSsd::event_arrive(enum event_type type, ulong logical_address, uint size, double start_time, void *buffer) 76 | { 77 | if (type == WRITE) 78 | printf("Writing to logical address: %lu\n", logical_address); 79 | else if (type == READ) 80 | printf("Read from logical address: %lu\n", logical_address); 81 | 82 | if (PARALLELISM_MODE == 1) // Striping 83 | { 84 | double timings[RAID_NUMBER_OF_PHYSICAL_SSDS]; 85 | for (int i=0;i. */ 17 | 18 | /****************************************************************************/ 19 | 20 | /* Ram class 21 | * 22 | * Brendan Tauras 2009-06-03 23 | * 24 | * This is a basic implementation that only provides delay updates to events 25 | * based on a delay value multiplied by the size (number of pages) needed to 26 | * be read or written. 27 | */ 28 | 29 | #include 30 | #include 31 | #include "ssd.h" 32 | 33 | using namespace ssd; 34 | 35 | 36 | Ram::Ram(double read_delay, double write_delay): 37 | read_delay(read_delay), 38 | write_delay(write_delay) 39 | { 40 | if(read_delay <= 0) 41 | { 42 | fprintf(stderr, "RAM: %s: constructor received negative read delay value\n\tsetting read delay to 0.0\n", __func__); 43 | read_delay = 0.0; 44 | } 45 | if(write_delay <= 0) 46 | { 47 | fprintf(stderr, "RAM: %s: constructor received negative write delay value\n\tsetting write delay to 0.0\n", __func__); 48 | write_delay = 0.0; 49 | } 50 | return; 51 | } 52 | 53 | Ram::~Ram(void) 54 | { 55 | return; 56 | } 57 | 58 | enum status Ram::read(Event &event) 59 | { 60 | assert(read_delay >= 0.0); 61 | (void) event.incr_time_taken(read_delay * event.get_size()); 62 | return SUCCESS; 63 | } 64 | 65 | enum status Ram::write(Event &event) 66 | { 67 | assert(write_delay >= 0.0); 68 | (void) event.incr_time_taken(write_delay * event.get_size()); 69 | return SUCCESS; 70 | } 71 | -------------------------------------------------------------------------------- /ssd_ssd.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2009, 2010 Brendan Tauras */ 2 | 3 | /* ssd_ssd.cpp is part of FlashSim. */ 4 | 5 | /* FlashSim is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * any later version. */ 9 | 10 | /* FlashSim is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. */ 14 | 15 | /* You should have received a copy of the GNU General Public License 16 | * along with FlashSim. If not, see . */ 17 | 18 | /****************************************************************************/ 19 | 20 | /* Ssd class 21 | * Brendan Tauras 2009-11-03 22 | * 23 | * The SSD is the single main object that will be created to simulate a real 24 | * SSD. Creating a SSD causes all other objects in the SSD to be created. The 25 | * event_arrive method is where events will arrive from DiskSim. */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include "ssd.h" 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | using namespace ssd; 40 | 41 | /* use caution when editing the initialization list - initialization actually 42 | * occurs in the order of declaration in the class definition and not in the 43 | * order listed here */ 44 | Ssd::Ssd(uint ssd_size): 45 | size(ssd_size), 46 | controller(*this), 47 | ram(RAM_READ_DELAY, RAM_WRITE_DELAY), 48 | bus(size, BUS_CTRL_DELAY, BUS_DATA_DELAY, BUS_TABLE_SIZE, BUS_MAX_CONNECT), 49 | 50 | /* use a const pointer (Package * const data) to use as an array 51 | * but like a reference, we cannot reseat the pointer */ 52 | data((Package *) malloc(ssd_size * sizeof(Package))), 53 | 54 | /* set erases remaining to BLOCK_ERASES to match Block constructor args 55 | * in Plane class 56 | * this is the cheap implementation but can change to pass through classes */ 57 | erases_remaining(BLOCK_ERASES), 58 | 59 | /* assume all Planes are same so first one can start as least worn */ 60 | least_worn(0), 61 | 62 | /* assume hardware created at time 0 and had an implied free erasure */ 63 | last_erase_time(0.0) 64 | { 65 | uint i; 66 | 67 | /* new cannot initialize an array with constructor args so 68 | * malloc the array 69 | * then use placement new to call the constructor for each element 70 | * chose an array over container class so we don't have to rely on anything 71 | * i.e. STL's std::vector */ 72 | /* array allocated in initializer list: 73 | * data = (Package *) malloc(ssd_size * sizeof(Package)); */ 74 | if(data == NULL){ 75 | fprintf(stderr, "Ssd error: %s: constructor unable to allocate Package data\n", __func__); 76 | exit(MEM_ERR); 77 | } 78 | for (i = 0; i < ssd_size; i++) 79 | { 80 | (void) new (&data[i]) Package(*this, bus.get_channel(i), PACKAGE_SIZE, PACKAGE_SIZE*DIE_SIZE*PLANE_SIZE*BLOCK_SIZE*i); 81 | } 82 | 83 | // Check for 32bit machine. We do not allow page data on 32bit machines. 84 | if (PAGE_ENABLE_DATA == 1 && sizeof(void*) == 4) 85 | { 86 | fprintf(stderr, "Ssd error: %s: The simulator requires a 64bit kernel when using data pages. Disabling data pages.\n", __func__); 87 | exit(MEM_ERR); 88 | } 89 | 90 | if (PAGE_ENABLE_DATA) 91 | { 92 | /* Allocate memory for data pages */ 93 | ulong pageSize = ((ulong)(SSD_SIZE * PACKAGE_SIZE * DIE_SIZE * PLANE_SIZE * BLOCK_SIZE)) * (ulong)PAGE_SIZE; 94 | #ifdef __APPLE__ 95 | page_data = mmap(NULL, pageSize, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0); 96 | #else 97 | page_data = mmap64(NULL, pageSize, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1 ,0); 98 | #endif 99 | 100 | if (page_data == MAP_FAILED) 101 | { 102 | fprintf(stderr, "Ssd error: %s: constructor unable to allocate page data.\n", __func__); 103 | switch (errno) 104 | { 105 | case EACCES: 106 | break; 107 | } 108 | printf("%i\n",errno); 109 | exit(MEM_ERR); 110 | } 111 | } 112 | 113 | assert(VIRTUAL_BLOCK_SIZE > 0); 114 | assert(VIRTUAL_PAGE_SIZE > 0); 115 | 116 | return; 117 | } 118 | 119 | Ssd::~Ssd(void) 120 | { 121 | /* explicitly call destructors and use free 122 | * since we used malloc and placement new */ 123 | for (uint i = 0; i < size; i++) 124 | { 125 | data[i].~Package(); 126 | } 127 | free(data); 128 | ulong pageSize = ((ulong)(SSD_SIZE * PACKAGE_SIZE * DIE_SIZE * PLANE_SIZE * BLOCK_SIZE)) * (ulong)PAGE_SIZE; 129 | munmap(page_data, pageSize); 130 | 131 | return; 132 | } 133 | 134 | double Ssd::event_arrive(enum event_type type, ulong logical_address, uint size, double start_time) 135 | { 136 | return event_arrive(type, logical_address, size, start_time, NULL); 137 | } 138 | 139 | /* This is the function that will be called by DiskSim 140 | * Provide the event (request) type (see enum in ssd.h), 141 | * logical_address (page number), size of request in pages, and the start 142 | * time (arrive time) of the request 143 | * The SSD will process the request and return the time taken to process the 144 | * request. Remember to use the same time units as in the config file. */ 145 | double Ssd::event_arrive(enum event_type type, ulong logical_address, uint size, double start_time, void *buffer) 146 | { 147 | assert(start_time >= 0.0); 148 | 149 | if (VIRTUAL_PAGE_SIZE == 1) 150 | assert((long long int) logical_address <= (long long int) SSD_SIZE * PACKAGE_SIZE * DIE_SIZE * PLANE_SIZE * BLOCK_SIZE); 151 | else 152 | assert((long long int) logical_address*VIRTUAL_PAGE_SIZE <= (long long int) SSD_SIZE * PACKAGE_SIZE * DIE_SIZE * PLANE_SIZE * BLOCK_SIZE); 153 | 154 | /* allocate the event and address dynamically so that the allocator can 155 | * handle efficiency issues for us */ 156 | Event *event = NULL; 157 | 158 | if((event = new Event(type, logical_address , size, start_time)) == NULL) 159 | { 160 | fprintf(stderr, "Ssd error: %s: could not allocate Event\n", __func__); 161 | exit(MEM_ERR); 162 | } 163 | 164 | event->set_payload(buffer); 165 | 166 | if(controller.event_arrive(*event) != SUCCESS) 167 | { 168 | fprintf(stderr, "Ssd error: %s: request failed:\n", __func__); 169 | event -> print(stderr); 170 | } 171 | 172 | /* use start_time as a temporary for returning time taken to service event */ 173 | start_time = event -> get_time_taken(); 174 | delete event; 175 | return start_time; 176 | } 177 | 178 | /* 179 | * Returns a pointer to the global buffer of the Ssd. 180 | * It is up to the user to not read out of bound and only 181 | * read the intended size. i.e. the page size. 182 | */ 183 | void *Ssd::get_result_buffer() 184 | { 185 | return global_buffer; 186 | } 187 | 188 | /* read write erase and merge should only pass on the event 189 | * the Controller should lock the bus channels 190 | * technically the Package is conceptual, but we keep track of statistics 191 | * and addresses with Packages, so send Events through Package but do not 192 | * have Package do anything but update its statistics and pass on to Die */ 193 | enum status Ssd::read(Event &event) 194 | { 195 | assert(data != NULL && event.get_address().package < size && event.get_address().valid >= PACKAGE); 196 | return data[event.get_address().package].read(event); 197 | } 198 | 199 | enum status Ssd::write(Event &event) 200 | { 201 | assert(data != NULL && event.get_address().package < size && event.get_address().valid >= PACKAGE); 202 | return data[event.get_address().package].write(event); 203 | } 204 | 205 | enum status Ssd::replace(Event &event) 206 | { 207 | if(event.get_replace_address().valid == NONE) 208 | return SUCCESS; 209 | assert(data != NULL && event.get_replace_address().package < size); 210 | if (event.get_replace_address().valid == PAGE) 211 | return data[event.get_replace_address().package].replace(event); 212 | else 213 | return SUCCESS; 214 | } 215 | 216 | 217 | enum status Ssd::erase(Event &event) 218 | { 219 | assert(data != NULL && event.get_address().package < size && event.get_address().valid >= PACKAGE); 220 | enum status status = data[event.get_address().package].erase(event); 221 | 222 | /* update values if no errors */ 223 | if (status == SUCCESS) 224 | update_wear_stats(event.get_address()); 225 | return status; 226 | } 227 | 228 | enum status Ssd::merge(Event &event) 229 | { 230 | assert(data != NULL && event.get_address().package < size && event.get_address().valid >= PACKAGE); 231 | return data[event.get_address().package].merge(event); 232 | } 233 | 234 | enum status Ssd::merge_replacement_block(Event &event) 235 | { 236 | //assert(data != NULL && event.get_address().package < size && event.get_address().valid >= PACKAGE && event.get_log_address().valid >= PACKAGE); 237 | return SUCCESS; 238 | } 239 | 240 | /* add up the erases remaining for all packages in the ssd*/ 241 | ssd::ulong Ssd::get_erases_remaining(const Address &address) const 242 | { 243 | assert (data != NULL); 244 | 245 | if (address.package < size && address.valid >= PACKAGE) 246 | return data[address.package].get_erases_remaining(address); 247 | else return erases_remaining; 248 | } 249 | 250 | void Ssd::update_wear_stats(const Address &address) 251 | { 252 | assert(data != NULL); 253 | uint i; 254 | uint max_index = 0; 255 | ulong max = data[0].get_erases_remaining(address); 256 | for(i = 1; i < size; i++) 257 | if(data[i].get_erases_remaining(address) > max) 258 | max_index = i; 259 | least_worn = max_index; 260 | erases_remaining = max; 261 | last_erase_time = data[max_index].get_last_erase_time(address); 262 | return; 263 | } 264 | 265 | void Ssd::get_least_worn(Address &address) const 266 | { 267 | assert(data != NULL && least_worn < size); 268 | address.package = least_worn; 269 | address.valid = PACKAGE; 270 | data[least_worn].get_least_worn(address); 271 | return; 272 | } 273 | 274 | double Ssd::get_last_erase_time(const Address &address) const 275 | { 276 | assert(data != NULL); 277 | if(address.package < size && address.valid >= PACKAGE) 278 | return data[address.package].get_last_erase_time(address); 279 | else 280 | return last_erase_time; 281 | } 282 | 283 | enum page_state Ssd::get_state(const Address &address) const 284 | { 285 | assert(data != NULL); 286 | assert(address.package < size && address.valid >= PACKAGE); 287 | return data[address.package].get_state(address); 288 | } 289 | 290 | enum block_state Ssd::get_block_state(const Address &address) const 291 | { 292 | assert(data != NULL); 293 | assert(address.package < size && address.valid >= PACKAGE); 294 | return data[address.package].get_block_state(address); 295 | } 296 | 297 | void Ssd::get_free_page(Address &address) const 298 | { 299 | assert(address.package < size && address.valid >= PACKAGE); 300 | data[address.package].get_free_page(address); 301 | return; 302 | } 303 | 304 | ssd::uint Ssd::get_num_free(const Address &address) const 305 | { 306 | return 0; 307 | /* return data[address.package].get_num_free(address); */ 308 | } 309 | 310 | ssd::uint Ssd::get_num_valid(const Address &address) const 311 | { 312 | assert(address.valid >= PACKAGE); 313 | return data[address.package].get_num_valid(address); 314 | } 315 | 316 | ssd::uint Ssd::get_num_invalid(const Address &address) const 317 | { 318 | assert(address.valid >= PACKAGE); 319 | return data[address.package].get_num_invalid(address); 320 | } 321 | 322 | void Ssd::print_statistics() 323 | { 324 | controller.stats.print_statistics(); 325 | } 326 | 327 | void Ssd::reset_statistics() 328 | { 329 | controller.stats.reset_statistics(); 330 | } 331 | 332 | void Ssd::write_statistics(FILE *stream) 333 | { 334 | controller.stats.write_statistics(stream); 335 | } 336 | 337 | void Ssd::print_ftl_statistics() 338 | { 339 | controller.print_ftl_statistics(); 340 | } 341 | 342 | void Ssd::write_header(FILE *stream) 343 | { 344 | controller.stats.write_header(stream); 345 | } 346 | 347 | Block *Ssd::get_block_pointer(const Address & address) 348 | { 349 | assert(address.valid >= PACKAGE); 350 | return data[address.package].get_block_pointer(address); 351 | } 352 | 353 | const Controller &Ssd::get_controller(void) const 354 | { 355 | return controller; 356 | } 357 | 358 | /** 359 | * Returns the next ready time. The ready time is the latest point in time when one of the channels are ready to serve new requests. 360 | */ 361 | double Ssd::ready_at(void) 362 | { 363 | double next_ready_time = std::numeric_limits::max(); 364 | 365 | for (int i=0;i::max()) 374 | return -1; 375 | else 376 | return next_ready_time; 377 | } 378 | -------------------------------------------------------------------------------- /ssd_stats.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2011 Matias Bjørling */ 2 | 3 | /* dftp_ftl.cpp */ 4 | 5 | /* FlashSim is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * any later version. */ 9 | 10 | /* FlashSim is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. */ 14 | 15 | /* You should have received a copy of the GNU General Public License 16 | * along with FlashSim. If not, see . */ 17 | 18 | /****************************************************************************/ 19 | 20 | /* Runtime information for the SSD Model 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include "ssd.h" 29 | 30 | using namespace ssd; 31 | 32 | Stats::Stats() 33 | { 34 | reset(); 35 | } 36 | 37 | void Stats::reset() 38 | { 39 | // FTL 40 | numFTLRead = 0; 41 | numFTLWrite = 0; 42 | numFTLErase = 0; 43 | numFTLTrim = 0; 44 | 45 | //GC 46 | numGCRead = 0; 47 | numGCWrite = 0; 48 | numGCErase = 0; 49 | 50 | // WL 51 | numWLRead = 0; 52 | numWLWrite = 0; 53 | numWLErase = 0; 54 | 55 | // Log based FTL's 56 | numLogMergeSwitch = 0; 57 | numLogMergePartial = 0; 58 | numLogMergeFull = 0; 59 | 60 | // Page based FTL's 61 | numPageBlockToPageConversion = 0; 62 | 63 | // Cache based FTL's 64 | numCacheHits = 0; 65 | numCacheFaults = 0; 66 | 67 | // Memory consumptions (Bytes) 68 | numMemoryTranslation = 0; 69 | numMemoryCache = 0; 70 | 71 | numMemoryRead = 0; 72 | numMemoryWrite = 0; 73 | } 74 | 75 | void Stats::reset_statistics() 76 | { 77 | reset(); 78 | } 79 | 80 | void Stats::write_header(FILE *stream) 81 | { 82 | fprintf(stream, "numFTLRead;numFTLWrite;numFTLErase;numFTLTrim;numGCRead;numGCWrite;numGCErase;numWLRead;numWLWrite;numWLErase;numLogMergeSwitch;numLogMergePartial;numLogMergeFull;numPageBlockToPageConversion;numCacheHits;numCacheFaults;numMemoryTranslation;numMemoryCache;numMemoryRead;numMemoryWrite\n"); 83 | } 84 | 85 | void Stats::write_statistics(FILE *stream) 86 | { 87 | fprintf(stream, "%li;%li;%li;%li;%li;%li;%li;%li;%li;%li;%li;%li;%li;%li;%li;%li;%li;%li;%li;%li;\n", 88 | numFTLRead, numFTLWrite, numFTLErase, numFTLTrim, 89 | numGCRead, numGCWrite, numGCErase, 90 | numWLRead, numWLWrite, numWLErase, 91 | numLogMergeSwitch, numLogMergePartial, numLogMergeFull, 92 | numPageBlockToPageConversion, 93 | numCacheHits, numCacheFaults, 94 | numMemoryTranslation, 95 | numMemoryCache, 96 | numMemoryRead,numMemoryWrite); 97 | 98 | //print_statistics(); 99 | } 100 | 101 | void Stats::print_statistics() 102 | { 103 | printf("Statistics:\n"); 104 | printf("-----------\n"); 105 | printf("FTL Reads: %li\t Writes: %li\t Erases: %li\t Trims: %li\n", numFTLRead, numFTLWrite, numFTLErase, numFTLTrim); 106 | printf("GC Reads: %li\t Writes: %li\t Erases: %li\n", numGCRead, numGCWrite, numGCErase); 107 | printf("WL Reads: %li\t Writes: %li\t Erases: %li\n", numWLRead, numWLWrite, numWLErase); 108 | printf("Log FTL Switch: %li Partial: %li Full: %li\n", numLogMergeSwitch, numLogMergePartial, numLogMergeFull); 109 | printf("Page FTL Convertions: %li\n", numPageBlockToPageConversion); 110 | printf("Cache Hits: %li Faults: %li Hit Ratio: %f\n", numCacheHits, numCacheFaults, (double)numCacheHits/(double)(numCacheHits+numCacheFaults)); 111 | printf("Memory Consumption:\n"); 112 | printf("Tranlation: %li Cache: %li\n", numMemoryTranslation, numMemoryCache); 113 | printf("Reads: %li \tWrites: %li\n", numMemoryRead, numMemoryWrite); 114 | printf("-----------\n"); 115 | } 116 | -------------------------------------------------------------------------------- /ssd_wl.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2009, 2010 Brendan Tauras */ 2 | 3 | /* ssd_wl.cpp is part of FlashSim. */ 4 | 5 | /* FlashSim is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * any later version. */ 9 | 10 | /* FlashSim is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. */ 14 | 15 | /* You should have received a copy of the GNU General Public License 16 | * along with FlashSim. If not, see . */ 17 | 18 | /****************************************************************************/ 19 | 20 | /* Wear_leveler class 21 | * Brendan Tauras 2009-11-04 22 | * 23 | * This class is a stub class for the user to use as a template for implementing 24 | * his/her wear leveler scheme. The wear leveler class was added to simplify 25 | * and modularize the wear-leveling in FTL schemes. */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include "ssd.h" 31 | 32 | using namespace ssd; 33 | 34 | Wear_leveler::Wear_leveler(FtlParent &ftl) 35 | { 36 | 37 | return; 38 | } 39 | 40 | Wear_leveler::~Wear_leveler(void) 41 | { 42 | return; 43 | } 44 | 45 | enum status Wear_leveler::insert(const Address &address) 46 | { 47 | return SUCCESS; 48 | } 49 | -------------------------------------------------------------------------------- /uml: -------------------------------------------------------------------------------- 1 | Copyright 2009, 2010 Brendan Tauras 2 | 3 | uml.* are part of FlashSim. 4 | 5 | FlashSim is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | any later version. 9 | 10 | FlashSim is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with FlashSim. If not, see . 17 | 18 | ############################################################################## 19 | 20 | -------------------------------------------------------------------------------- /uml.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatiasBjorling/flashsim/53098b017a4dbbe27665a755000738ed70862141/uml.dia -------------------------------------------------------------------------------- /uml.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatiasBjorling/flashsim/53098b017a4dbbe27665a755000738ed70862141/uml.pdf -------------------------------------------------------------------------------- /verification.cpp: -------------------------------------------------------------------------------- 1 | /* FlashSim is free software: you can redistribute it and/or modify 2 | * it under the terms of the GNU General Public License as published by 3 | * the Free Software Foundation, either version 3 of the License, or 4 | * any later version. */ 5 | 6 | /* FlashSim is distributed in the hope that it will be useful, 7 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | * GNU General Public License for more details. */ 10 | 11 | /* You should have received a copy of the GNU General Public License 12 | * along with FlashSim. If not, see . */ 13 | 14 | /****************************************************************************/ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "ssd.h" 26 | 27 | using namespace ssd; 28 | 29 | int main(int argc, char **argv){ 30 | 31 | long vaddr; 32 | 33 | double arrive_time; 34 | 35 | double afterFormatStartTime = 0; 36 | 37 | load_config(); 38 | print_config(NULL); 39 | 40 | Ssd ssd; 41 | 42 | printf("INITIALIZING SSD\n"); 43 | 44 | srandom(1); 45 | int preIO = SSD_SIZE * PACKAGE_SIZE * DIE_SIZE * PLANE_SIZE * BLOCK_SIZE; 46 | 47 | if (FTL_IMPLEMENTATION == 0) // PAGE 48 | preIO -= 16*BLOCK_SIZE; 49 | 50 | if (FTL_IMPLEMENTATION == 1) // BAST 51 | preIO -= (BAST_LOG_BLOCK_LIMIT*BLOCK_SIZE)*1.3; 52 | 53 | if (FTL_IMPLEMENTATION == 2) // FAST 54 | preIO -= (FAST_LOG_BLOCK_LIMIT*BLOCK_SIZE)*1.1; 55 | 56 | if (FTL_IMPLEMENTATION > 2) // DFTL BIFTL 57 | preIO -= 1024; 58 | 59 | int deviceSize = 3145216; 60 | 61 | if (preIO > deviceSize) 62 | preIO = deviceSize; 63 | 64 | printf("Writes %i pages for startup out of %i total pages.\n", preIO, SSD_SIZE * PACKAGE_SIZE * DIE_SIZE * PLANE_SIZE * BLOCK_SIZE); 65 | 66 | double start_time = afterFormatStartTime; 67 | double timeMultiplier = 10000; 68 | 69 | FILE *logFile = NULL; 70 | if ((logFile = fopen("output.log", "w")) == NULL) 71 | { 72 | printf("Output file cannot be written to.\n"); 73 | exit(-1); 74 | } 75 | 76 | fprintf(logFile, "NumIOReads;ReadIOTime;NumIOWrites;WriteIOTime;NumIOTotal;IOTime;"); 77 | ssd.write_header(logFile); 78 | 79 | double read_time = 0; 80 | double write_time = 0; 81 | 82 | unsigned long num_reads = 0; 83 | unsigned long num_writes = 0; 84 | 85 | std::vector avgs; 86 | 87 | // Reset statistics 88 | ssd.reset_statistics(); 89 | 90 | num_reads = 0; 91 | read_time = 0; 92 | 93 | num_writes = 0; 94 | write_time = 0; 95 | 96 | int addressDivisor = 1; 97 | float multiplier = 1; 98 | 99 | //ssd.reset_statistics(); 100 | 101 | avgs.reserve(preIO); 102 | 103 | 104 | 105 | // /* Test 1 */ 106 | // for (int i=0; i preIO/2-seqSize/2 && preIO/2+seqSize/2 > r); 148 | // 149 | // write_time = ssd.event_arrive(WRITE, r, 1, ((start_time+arrive_time)*timeMultiplier)); 150 | // avgs.push_back(write_time); 151 | // num_writes++; 152 | // 153 | // arrive_time += write_time; 154 | // 155 | // if (i % 100000 == 0) 156 | // printf("%i\n", i); 157 | // } 158 | 159 | ssd.print_ftl_statistics(); 160 | 161 | /* Test 3 Start -------------------------------------------------------------------------- */ 162 | srandom(1); 163 | int seqSize = 128*64; 164 | 165 | for (int i=0; i