// File: /workspace/github_cpp_makefile_star2_O2/CMU-SAFARI[P]MQSim/src/ssd/GC_and_WL_Unit_Base.cpp
// Function: SSD_Components::GC_and_WL_Unit_Base::handle_transaction_serviced_signal_from_PHY(SSD_Components::NVM_Transaction_Flash*)
void GC_and_WL_Unit_Base::handle_transaction_serviced_signal_from_PHY(NVM_Transaction_Flash* transaction)
	{
		PlaneBookKeepingType* pbke = &(_my_instance->block_manager->plane_manager[transaction->Address.ChannelID][transaction->Address.ChipID][transaction->Address.DieID][transaction->Address.PlaneID]);

		switch (transaction->Source) {
			case Transaction_Source_Type::USERIO:
			case Transaction_Source_Type::MAPPING:
			case Transaction_Source_Type::CACHE:
				switch (transaction->Type)
				{
					case Transaction_Type::READ:
						_my_instance->block_manager->Read_transaction_serviced(transaction->Address);
						break;
					case Transaction_Type::WRITE:
						_my_instance->block_manager->Program_transaction_serviced(transaction->Address);
						break;
					default:
						PRINT_ERROR("Unexpected situation in the GC_and_WL_Unit_Base function!")
				}
				if (_my_instance->block_manager->Block_has_ongoing_gc_wl(transaction->Address)) {
					if (_my_instance->block_manager->Can_execute_gc_wl(transaction->Address)) {
						NVM::FlashMemory::Physical_Page_Address gc_wl_candidate_address(transaction->Address);
						Block_Pool_Slot_Type* block = &pbke->Blocks[transaction->Address.BlockID];
						Stats::Total_gc_executions++;
						_my_instance->tsu->Prepare_for_transaction_submit();
						NVM_Transaction_Flash_ER* gc_wl_erase_tr = new NVM_Transaction_Flash_ER(Transaction_Source_Type::GC_WL, block->Stream_id, gc_wl_candidate_address);
						
						//If there are some valid pages in block, then prepare flash transactions for page movement
						if (block->Current_page_write_index - block->Invalid_page_count > 0) {
							//address_mapping_unit->Lock_physical_block_for_gc(gc_candidate_address);//Lock the block, so no user request can intervene while the GC is progressing
							NVM_Transaction_Flash_RD* gc_wl_read = NULL;
							NVM_Transaction_Flash_WR* gc_wl_write = NULL;
							for (flash_page_ID_type pageID = 0; pageID < block->Current_page_write_index; pageID++) {
								if (_my_instance->block_manager->Is_page_valid(block, pageID)) {
									Stats::Total_page_movements_for_gc++;
									gc_wl_candidate_address.PageID = pageID;
									if (_my_instance->use_copyback) {
										gc_wl_write = new NVM_Transaction_Flash_WR(Transaction_Source_Type::GC_WL, block->Stream_id, _my_instance->sector_no_per_page * SECTOR_SIZE_IN_BYTE,
											NO_LPA, _my_instance->address_mapping_unit->Convert_address_to_ppa(gc_wl_candidate_address), NULL, 0, NULL, 0, INVALID_TIME_STAMP);
										gc_wl_write->ExecutionMode = WriteExecutionModeType::COPYBACK;
										_my_instance->tsu->Submit_transaction(gc_wl_write);
									} else {
										gc_wl_read = new NVM_Transaction_Flash_RD(Transaction_Source_Type::GC_WL, block->Stream_id, _my_instance->sector_no_per_page * SECTOR_SIZE_IN_BYTE,
											NO_LPA, _my_instance->address_mapping_unit->Convert_address_to_ppa(gc_wl_candidate_address), gc_wl_candidate_address, NULL, 0, NULL, 0, INVALID_TIME_STAMP);
										gc_wl_write = new NVM_Transaction_Flash_WR(Transaction_Source_Type::GC_WL, block->Stream_id, _my_instance->sector_no_per_page * SECTOR_SIZE_IN_BYTE,
											NO_LPA, NO_PPA, gc_wl_candidate_address, NULL, 0, gc_wl_read, 0, INVALID_TIME_STAMP);
										gc_wl_write->ExecutionMode = WriteExecutionModeType::SIMPLE;
										gc_wl_write->RelatedErase = gc_wl_erase_tr;
										gc_wl_read->RelatedWrite = gc_wl_write;
										_my_instance->tsu->Submit_transaction(gc_wl_read);//Only the read transaction would be submitted. The Write transaction is submitted when the read transaction is finished and the LPA of the target page is determined
									}
									gc_wl_erase_tr->Page_movement_activities.push_back(gc_wl_write);
								}
							}
						}
						block->Erase_transaction = gc_wl_erase_tr;
						_my_instance->tsu->Schedule();
					}
				}

				return;
		}

		switch (transaction->Type) {
			case Transaction_Type::READ:
			{
				PPA_type ppa;
				MPPN_type mppa;
				page_status_type page_status_bitmap;
				if (pbke->Blocks[transaction->Address.BlockID].Holds_mapping_data) {
					_my_instance->address_mapping_unit->Get_translation_mapping_info_for_gc(transaction->Stream_id, (MVPN_type)transaction->LPA, mppa, page_status_bitmap);
					//There has been no write on the page since GC start, and it is still valid
					if (mppa == transaction->PPA) {
						_my_instance->tsu->Prepare_for_transaction_submit();
						((NVM_Transaction_Flash_RD*)transaction)->RelatedWrite->write_sectors_bitmap = FULL_PROGRAMMED_PAGE;
						((NVM_Transaction_Flash_RD*)transaction)->RelatedWrite->LPA = transaction->LPA;
						((NVM_Transaction_Flash_RD*)transaction)->RelatedWrite->RelatedRead = NULL;
						_my_instance->address_mapping_unit->Allocate_new_page_for_gc(((NVM_Transaction_Flash_RD*)transaction)->RelatedWrite, pbke->Blocks[transaction->Address.BlockID].Holds_mapping_data);
						_my_instance->tsu->Submit_transaction(((NVM_Transaction_Flash_RD*)transaction)->RelatedWrite);
						_my_instance->tsu->Schedule();
					} else {
						PRINT_ERROR("Inconsistency found when moving a page for GC/WL!")
					}
				} else {
					_my_instance->address_mapping_unit->Get_data_mapping_info_for_gc(transaction->Stream_id, transaction->LPA, ppa, page_status_bitmap);
					
					//There has been no write on the page since GC start, and it is still valid
					if (ppa == transaction->PPA) {
						_my_instance->tsu->Prepare_for_transaction_submit();
						((NVM_Transaction_Flash_RD*)transaction)->RelatedWrite->write_sectors_bitmap = page_status_bitmap;
						((NVM_Transaction_Flash_RD*)transaction)->RelatedWrite->LPA = transaction->LPA;
						((NVM_Transaction_Flash_RD*)transaction)->RelatedWrite->RelatedRead = NULL;
						_my_instance->address_mapping_unit->Allocate_new_page_for_gc(((NVM_Transaction_Flash_RD*)transaction)->RelatedWrite, pbke->Blocks[transaction->Address.BlockID].Holds_mapping_data);
						_my_instance->tsu->Submit_transaction(((NVM_Transaction_Flash_RD*)transaction)->RelatedWrite);
						_my_instance->tsu->Schedule();
					} else {
						PRINT_ERROR("Inconsistency found when moving a page for GC/WL!")
					}
				}
				break;
			}
			case Transaction_Type::WRITE:
				if (pbke->Blocks[((NVM_Transaction_Flash_WR*)transaction)->RelatedErase->Address.BlockID].Holds_mapping_data) {
					_my_instance->address_mapping_unit->Remove_barrier_for_accessing_mvpn(transaction->Stream_id, (MVPN_type)transaction->LPA);
					DEBUG(Simulator->Time() << ": MVPN=" << (MVPN_type)transaction->LPA << " unlocked!!");
				} else {
					_my_instance->address_mapping_unit->Remove_barrier_for_accessing_lpa(transaction->Stream_id, transaction->LPA);
					DEBUG(Simulator->Time() << ": LPA=" << (MVPN_type)transaction->LPA << " unlocked!!");
				}
				pbke->Blocks[((NVM_Transaction_Flash_WR*)transaction)->RelatedErase->Address.BlockID].Erase_transaction->Page_movement_activities.remove((NVM_Transaction_Flash_WR*)transaction);
				break;
			case Transaction_Type::ERASE:
				pbke->Ongoing_erase_operations.erase(pbke->Ongoing_erase_operations.find(transaction->Address.BlockID));
				_my_instance->block_manager->Add_erased_block_to_pool(transaction->Address);
				_my_instance->block_manager->GC_WL_finished(transaction->Address);
				if (_my_instance->check_static_wl_required(transaction->Address)) {
					_my_instance->run_static_wearleveling(transaction->Address);
				}
				_my_instance->address_mapping_unit->Start_servicing_writes_for_overfull_plane(transaction->Address);//Must be inovked after above statements since it may lead to flash page consumption for waiting program transactions

				if (_my_instance->Stop_servicing_writes(transaction->Address)) {
					_my_instance->Check_gc_required(pbke->Get_free_block_pool_size(), transaction->Address);
				}
				break;
			} //switch (transaction->Type)
	}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0xc8, %rsp
movq %rdi, %r14
movq 0x2f7fb(%rip), %rax     # 0x62618
movq 0x38(%rax), %rdi
movq 0x8(%rdi), %rdx
leaq 0x38(%r14), %r15
movl 0x4(%r14), %ecx
movl 0x38(%r14), %esi
movq (%rdx,%rsi,8), %rdx
movl 0x3c(%r14), %esi
movq (%rdx,%rsi,8), %rdx
movl 0x40(%r14), %esi
movl 0x44(%r14), %r8d
imulq $0xe0, %r8, %rbx
addq (%rdx,%rsi,8), %rbx
cmpl $0x3, %ecx
ja 0x32f3b
cmpl $0x2, %ecx
je 0x32f3b
movl 0x8(%r14), %eax
cmpl $0x1, %eax
je 0x32e7d
testl %eax, %eax
jne 0x333b7
movq %r15, %rsi
callq 0x31bf2
jmp 0x32e85
movq %r15, %rsi
callq 0x31bbe
movq 0x2f78c(%rip), %rax     # 0x62618
movq 0x38(%rax), %rdi
movq %r15, %rsi
callq 0x31ab4
testb %al, %al
je 0x333a5
movq 0x2f771(%rip), %rax     # 0x62618
movq 0x38(%rax), %rdi
movq %r15, %rsi
callq 0x31ae8
testb %al, %al
je 0x333a5
movups 0x38(%r14), %xmm0
leaq 0x50(%rsp), %r13
movaps %xmm0, (%r13)
movq 0x48(%r14), %rax
movq %rax, 0x10(%r13)
movq 0x10(%rbx), %r12
leaq 0x3084f(%rip), %rcx     # 0x6372c
incl (%rcx)
movl %eax, %r14d
shlq $0x6, %r14
movq 0x2f72b(%rip), %rax     # 0x62618
movq 0x40(%rax), %rdi
callq 0x264ce
movl $0x98, %edi
callq 0x7370
movzwl 0x20(%r12,%r14), %edx
pushq $0x2
popq %rsi
movq %rax, 0x38(%rsp)
movq %rax, %rdi
movq %r13, %rcx
callq 0x3a0dc
addq %r14, %r12
movl 0x4(%r12), %eax
cmpl 0xc(%r12), %eax
jne 0x32fb8
movq 0x38(%rsp), %rax
movq %rax, 0x28(%r12)
jmp 0x33394
movl 0x8(%r14), %ecx
cmpl $0x2, %ecx
je 0x331d0
cmpl $0x1, %ecx
je 0x33196
testl %ecx, %ecx
jne 0x333a5
movq 0x10(%rbx), %rcx
movl 0x48(%r14), %esi
shlq $0x6, %rsi
movq 0x30(%rax), %rdi
movzwl (%r14), %eax
movq 0x58(%r14), %rdx
cmpb $0x1, 0x22(%rcx,%rsi)
jne 0x33304
movq (%rdi), %r9
movzwl %ax, %esi
leaq 0x50(%rsp), %r15
leaq 0x30(%rsp), %r8
movq %r15, %rcx
callq *0x70(%r9)
movl (%r15), %eax
cmpq %rax, 0x60(%r14)
jne 0x333f8
movq 0x2f671(%rip), %rax     # 0x62618
movq 0x40(%rax), %rdi
callq 0x264ce
pushq $-0x1
popq %rax
jmp 0x3333d
leaq 0x30(%rsp), %rcx
andq $0x0, (%rcx)
movq 0x38(%rsp), %rcx
subq $-0x80, %rcx
movq %rcx, 0x48(%rsp)
xorl %ebp, %ebp
movq %r12, 0x40(%rsp)
cmpl %eax, %ebp
jae 0x32f2c
movq 0x2f633(%rip), %rax     # 0x62618
movq 0x38(%rax), %rdi
movq %r12, %rsi
movl %ebp, %edx
callq 0x31c92
testb %al, %al
je 0x3318a
movq %r13, %rbx
leaq 0x30b2b(%rip), %rax     # 0x63b30
incl (%rax)
movl %ebp, 0x64(%rsp)
movq 0x2f606(%rip), %r13     # 0x62618
cmpb $0x1, 0x64(%r13)
jne 0x3308f
movl $0xb0, %edi
callq 0x7370
movq %rax, %r15
movzwl 0x20(%r12), %r12d
movl 0x10c(%r13), %r14d
movq 0x30(%r13), %rdi
movq (%rdi), %rax
movq %rbx, %r13
movq %rbx, %rsi
callq *0x90(%rax)
pushq $0x2
popq %rsi
pushq $-0x1
popq %r8
shll $0x9, %r14d
xorps %xmm0, %xmm0
movups %xmm0, 0x10(%rsp)
movups %xmm0, (%rsp)
orq $-0x1, 0x20(%rsp)
movzwl %r12w, %edx
movq %r15, %rdi
movl %r14d, %ecx
movq %rax, %r9
callq 0x3a2c6
movq %r15, 0x30(%rsp)
movl $0x1, 0xa8(%r15)
movq 0x40(%rsp), %r12
jmp 0x33168
movl $0xa0, %edi
callq 0x7370
movq %rax, %r15
movzwl 0x20(%r12), %r12d
movl 0x10c(%r13), %r14d
movq 0x30(%r13), %rdi
movq (%rdi), %rax
movq %rbx, %r13
movq %rbx, %rsi
callq *0x90(%rax)
pushq $-0x1
popq %rbx
shll $0x9, %r14d
xorps %xmm0, %xmm0
movups %xmm0, 0x18(%rsp)
movups %xmm0, 0x8(%rsp)
orq $-0x1, 0x28(%rsp)
movq %r13, (%rsp)
movzwl %r12w, %edx
movq %r15, %rdi
pushq $0x2
popq %rsi
movl %r14d, %ecx
movq %rbx, %r8
movq %rax, %r9
callq 0x3a176
movl $0xb0, %edi
callq 0x7370
movq %rax, %r14
movq 0x40(%rsp), %r12
movzwl 0x20(%r12), %edx
movq 0x2f507(%rip), %rax     # 0x62618
movl 0x10c(%rax), %ecx
shll $0x9, %ecx
movq %r15, 0x18(%rsp)
xorps %xmm0, %xmm0
movups %xmm0, 0x8(%rsp)
orq $-0x1, 0x28(%rsp)
andq $0x0, 0x20(%rsp)
movq %r13, (%rsp)
movq %r14, %rdi
pushq $0x2
popq %rsi
movq %rbx, %r8
movq %rbx, %r9
callq 0x3a270
andl $0x0, 0xa8(%r14)
movq %r14, 0x30(%rsp)
movq 0x38(%rsp), %rax
movq %rax, 0x90(%r14)
movq %r14, 0x88(%r15)
movq 0x2f4a9(%rip), %rax     # 0x62618
movq 0x40(%rax), %rdi
movq %r15, %rsi
callq 0x264ec
movq 0x48(%rsp), %rdi
leaq 0x30(%rsp), %rsi
callq 0x3381a
incl %ebp
movl 0x4(%r12), %eax
jmp 0x32fd6
movq 0x10(%rbx), %rcx
movq 0x90(%r14), %rdx
movl 0x48(%rdx), %esi
shlq $0x6, %rsi
movq 0x30(%rax), %rdi
movzwl (%r14), %eax
movq 0x58(%r14), %rdx
cmpb $0x1, 0x22(%rcx,%rsi)
jne 0x332cb
movq (%rdi), %rcx
movzwl %ax, %esi
callq *0xb8(%rcx)
jmp 0x332d7
leaq 0xb0(%rbx), %r13
leaq 0x48(%r14), %r12
movq %r13, %rdi
movq %r12, %rsi
callq 0x28510
movq %r13, %rdi
movq %rax, %rsi
callq 0x2779e
movq 0x2f420(%rip), %rax     # 0x62618
movq 0x38(%rax), %rdi
movq (%rdi), %rax
movq %r15, %rsi
callq *0x40(%rax)
movq 0x2f40c(%rip), %rax     # 0x62618
movq 0x38(%rax), %rdi
movq %r15, %rsi
callq 0x31c5e
movq 0x2f3f9(%rip), %rdi     # 0x62618
movups 0x38(%r14), %xmm0
leaq 0xb0(%rsp), %rsi
movaps %xmm0, (%rsi)
movq 0x48(%r14), %rax
movq %rax, 0x10(%rsi)
callq 0x33884
testb %al, %al
je 0x33263
movq 0x2f3d1(%rip), %rdi     # 0x62618
movups (%r15), %xmm0
leaq 0x90(%rsp), %rsi
movaps %xmm0, (%rsi)
movq (%r12), %rax
movq %rax, 0x10(%rsi)
callq 0x3343e
movq 0x2f3ae(%rip), %rax     # 0x62618
movq 0x30(%rax), %rdi
movups (%r15), %xmm0
leaq 0x70(%rsp), %rsi
movaps %xmm0, (%rsi)
movq (%r12), %rax
movq %rax, 0x10(%rsi)
movq (%rdi), %rax
callq *0xc0(%rax)
movq 0x2f386(%rip), %r14     # 0x62618
movq 0x38(%r14), %rdi
movq (%rdi), %rax
movq %r15, %rsi
callq *0x48(%rax)
cmpl 0x7c(%r14), %eax
jae 0x333a5
movq 0x2f368(%rip), %r14     # 0x62618
movq %rbx, %rdi
callq 0x31998
movq (%r14), %rcx
movq %r14, %rdi
movl %eax, %esi
movq %r15, %rdx
callq *0x28(%rcx)
jmp 0x333a5
movq (%rdi), %rcx
movzwl %ax, %esi
callq *0xb0(%rcx)
movq 0x10(%rbx), %rax
movq 0x90(%r14), %rcx
movl 0x48(%rcx), %ecx
shlq $0x6, %rcx
movq 0x28(%rax,%rcx), %rdi
subq $-0x80, %rdi
leaq 0x50(%rsp), %rsi
movq %r14, (%rsi)
callq 0x33826
jmp 0x333a5
movq (%rdi), %r9
movzwl %ax, %esi
leaq 0x50(%rsp), %r15
leaq 0x30(%rsp), %r8
movq %r15, %rcx
callq *0x68(%r9)
movq (%r15), %rax
cmpq 0x60(%r14), %rax
jne 0x333f8
movq 0x2f2e9(%rip), %rax     # 0x62618
movq 0x40(%rax), %rdi
callq 0x264ce
movq 0x30(%rsp), %rax
movq 0x88(%r14), %rsi
movq %rax, 0x98(%rsi)
movq 0x58(%r14), %rax
movq %rax, 0x58(%rsi)
andq $0x0, 0x88(%rsi)
movq 0x2f2b6(%rip), %rax     # 0x62618
movq 0x30(%rax), %rdi
movq 0x10(%rbx), %rax
movl 0x48(%r14), %ecx
shlq $0x6, %rcx
movzbl 0x22(%rax,%rcx), %edx
movq (%rdi), %rax
callq *0x78(%rax)
movq 0x2f294(%rip), %rax     # 0x62618
movq 0x40(%rax), %rdi
movq 0x88(%r14), %rsi
callq 0x264ec
movq 0x2f27d(%rip), %rax     # 0x62618
movq 0x40(%rax), %rdi
movq (%rdi), %rax
callq *0x30(%rax)
addq $0xc8, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq 0x2ec32(%rip), %rbx     # 0x61ff0
leaq 0x111c3(%rip), %rsi     # 0x44588
movq %rbx, %rdi
callq 0x7360
leaq 0x1485b(%rip), %rsi     # 0x47c2f
movq %rbx, %rdi
callq 0x7360
movq %rax, %rdi
callq 0x71a0
movq 0x2ebe5(%rip), %rdi     # 0x61fd0
callq 0x73d0
pushq $0x1
popq %rdi
callq 0x73e0
movq 0x2ebf1(%rip), %rbx     # 0x61ff0
leaq 0x11182(%rip), %rsi     # 0x44588
movq %rbx, %rdi
callq 0x7360
leaq 0x14854(%rip), %rsi     # 0x47c69
jmp 0x333d4
movq %rax, %rbx
movq 0x38(%rsp), %rdi
jmp 0x33431
movq %rax, %rbx
movq %r14, %r15
jmp 0x3342e
jmp 0x3342b
movq %rax, %rbx
movq %r15, %rdi
callq 0x7340
movq %rbx, %rdi
callq 0x75d0