Skip to content

Macsim 정리

Code 진행 순서

sim=new macsim_c(); 로 생성
sim initialize 거침
init_knobs
init_sim
현재 시각을 m_begin_sim에 저장
각 variable size가 맞는지를 assert문을 통해 확인(ex) uns8, uns16, int8, etc.)
init_cores(int num_maxcore=m_num_sim_cores)
m_num_sim_cores = KNOB에서 small cores+medium cores+large cores 인지 check
macsim_c variable에 core_c *m_core_pointers 변수가 있는데, num_large_cores 개수만큼 각 pointer에 new core_c 할당. 그 이후 init(), pref_init()
init_memory
pool allocation: thread, section, mem_map_entry, heartbeat, bp_recovery_info, trace_node, uop, block_id_mapper, process_manager, trace_reader 새로 생성
init_output_streams
init_network
init_clock_domain
m_MMU 생성 이후, m_MMU->initialize
init_io
open traces
while(sim->run_a_cycle()); → sim termination 조건 만족하기 전까지 계속 돔
m_bme(bme_wrapper)→run_a_cycle
m_cxlsim→run_a_cycle
m_bme(cxl_t3)→run_a_cycle
rx_trans()
rx_dll()
rx_phy() (아무것도 없음!)
process_mem
m_done_q 안의 req들에 대해 iteration을 돌면서 try_send_req(req)을 시도함. try_send_req(req) 내에서는 현재 기준으로 accepted=m_pcmc_wrapper→send(req)를 시도하는데, 만약 이게 true일 경우 ready라는 뜻이므로 req→m_bme_start=m_cycle을 통해 bme_start한 시점을 현재 cycle로 등록하고, d_it=m_done_q.erase(d_it)을 통해 현재 req를 m_done_q에서 삭제한 뒤, 바로 뒤의 req을 d_it의 값으로 반환. 만약에 accepted의 값이 false일 경우 not ready이고, 이 경우 그냥 escape.
CXLPCMCsimWrapper::send(req)에서는 ready=memsys→isReady(pkt)를 통해 ready 여부를 확인하는데, memsys는 pcmcsim/base/MemoryControlSystem.h에 있는 class MemoryControlSystem이고, 여기 안의 함수는 바로 parser 내부의 함수를 호출함. parser는 pcmcsim/Parsers/Parser.h에 정의 되어 있고, Parser.cpp에서 함수들을 찾아볼 수 있음.
먼저 send 함수 내에서 ready임이 확인될 경우 memsys→recvRequest(pkt, 1)을 호출함. 첫번째 argument는 보다시피 pkt이고, 두번째 argument는 delay임. 이는 곧바로 parser→recvRequest(pkt, delay)를 호출함. 그러면 그 안에서 memsys의 sys_name이 DRAM일 경우 channel의 값을 ch에 저장한 뒤, 또 paths[ch]→recvRequest(pkt, delay)를 호출함. paths는 std::vector<Component*>로 돼있는데 class Component는 pcmcsim/base/Component.h에 정의돼 있음.
Component::recvRequest에서 실제로 뭔가가 일어남! wakeup cycle을 계산하고, 받은 pkt을 바탕으로 new event generation. 그 다음에 req_events.insert를 통해 stack event, geq→insertEvent를 통해 schedule event.
위의 과정이 끝나면 outstandPkts.insert를 통해서 packet과 req의 pair쌍을 저장함
accepted 값이 true든 false든 나오고 나면, accepted 값이 true인 경우에 한해서만 is_write 값을 보고 write/read를 구분해서 m_bme_writes나 m_bme_reads의 req→m_id 번째 index에 req를 저장하고, m_bme_requestsInFlight를 increment.
tx_phy() (아무것도 없음!)
tx_dll()
tx_trans()
m_rc→run_a_cycle
m_bme→run_a_cycle_internal
m_pcmc_wrapper→tick()

m_cycle_internal++
m_rc→pop_request (done requests finish하기)
m_rc→request_done
m_cycle++
m_MMU→handle_page_faults
batch processing 이미 진행중이라면(m_batch_processing), do_batch_processing()을 하고, 이게 안 끝났다면 return, 끝났다면 begin_batch_processing(). 참고로 한번에 하나의 request만 처리됨. Page fault hadling이 끝난 request는 m_retry_queue에 넣음. begin_batch_processing()의 과정은 아래 iii.에서 1~3번으로 서술되어 있음.
page fault가 발생하지 않았을 경우(m_fault_buffer.empty()) 그냥 return
위의 경우가 아니면 begin_batch_processing()
batch process가 진행중이라는 의미의 m_batch_processing이 false임을 assert
m_batch_processing = true;
m_batch_processing_start_cycle에다가 현재 m_cycle과 m_batch_processing_overhead 값을 합친 값을 저장
m_MMU→run_a_cycle
m_retry_queue(page fault handling 끝난 request가 들어 있는 곳)이 비어있지 않을 경우 retry를 실행.
여기에서는 mmu_c::do_page_table_walks(uop) 호출
PTW hit일 경우 TLB update
PTW miss(Page fault)일 경우 m_fault_buffer에 fault request를 넣음.
m_network→run_a_cycle
m_memory→run_a_cycle (m_clock_internal == m_domain_next[CLOCK_LLC]인 경우)
for each mc in number of controllers
m_dram_controller[ii]→run_a_cycle (밑에서 pcmcsim 기준으로 설명) (m_clock_internal == m_domain_next[CLOCK_MC]인 경우)
receive()를 호출하는데, 해당 함 수 내에선 먼저 NETWORK→receive로 req를 받아옴. m_router→receive_req에서 m_req_buffer가 empty라면 NULL 값을 반환하게 되고, receive 함수도 종료됨. 그렇지 않다면 m_req_buffer→front()로 req를 받아오게 됨. req→m_addr을 바탕으로 req이 BME request인지 판단하고 만약에 그렇다면 receive_bme_req(req)를, 그렇지 않다면 receive_dimm_req(req)를 호출
(case BME) receive_bme_req는 bme_wrapper→insert_request를 호출하고, 거기 안에서 바로 m_cxlsim→insert_request를 호출.
m_cxlsim→insert_request에서는 m_rc가 not ready일 경우 0을 반환하고, 이에 따라 false값을 반환하면 m_dram_controller→receive_bme_req에서 아무 것도 하지 않게 됨. 그게 아니라면, void* 형태의 req을 cxl에 맞는 형태의 req로 변환하고 m_rc→insert_request 호출
m_rc→insert_request 안에서는 cxl_req_s* req 안의 variable인 m_req_start와 m_rc_tx_start를 현재 cycle인 m_cycle 값으로 할당하고, m_pending_q에 push_back함.
insert_request가 끝나고 request를 받아올 수 있다는 것이 확인 되면 NETWORK→receive_pop을 호출해서 head request를 pop
(case DIMM) receive_dimm_req
NETWORK→receive_pop
for each core in number of processor cores
m_memory→run_a_cycle_core
core→run_a_cycle
[Execution stage] m_exec→run_a_cycle: USING_SST가 정의돼있지 않아 그냥 pass해도 됨
[Retire stage] m_retire→run_a_cycle
[Prefetcher] m_hw_pref→pref_update_queues(): m_knob_enable_pref parameter가 false 여서 현재는 실행 안되고 있음
[Scheduler] m_schedule→run_a_cycle
m_schedule은 core type에 따라서 schedule_smc, schedule_igpu, schedule_ooo, schedule_io로 나뉘어지는데, 현재 hynix setting에서는 schedule_ooo_c임
schedule_ooo_c::run_a_cycle 내에서 schedule_c::uop_schedule을 호출하고, 이 함수 내에서 exec_c::exec을 호출(schedule.cc에서 “// execute current uop”로 검색해보면 나옴)
exec_c::exec에서 MEMORY→access가 있는데, MEMORY는 macsim.h에 #define MEMORY m_simBase→m_memory로 되어 있음
memory_c::access(uop)는 결국 m_l1_cache[uop→m_core_id]→access(uop)를 호출, 즉 dcu_c** 형태의 variable인 L1 cache를 접근하게 됨
dcu_c::access()
m_MMU→translate(uop)로 mmu를 접근해서 address translation을 시도. 여기에서는 TLB hit/miss를 가장 먼저 판단. MMU::translate 내부에서는 bool tlb_hit =m_TLB→lookup(addr, pid)가 있는데, 만약 TLB miss일 경우에는 m_walk_queue_cycle에다가 request를 넣고, PTW를 준비한다. 이 경우 uop의 m_state가 OS_TRANS_WALK_QUEUE로 바뀌게 되고, 원래의 dcu_c::access()에서는 return -1로 나가게 되어 TLB miss를 cache miss보다 더 긴 latency로 취급하게 됨. 만약 TLB hit일 경우 uop의 m_state를 OS_DCACHE_BEGIN으로 바꾸고, DCACHE 접근 시작.
Read port와 Write port가 busy한지를 보고, busy하면 return 0을 해서 다시 access 호출해서 cache access 시도
Cache hit: store인 경우에는 line→m_dirty=true로 dirty bit을 set하고 latency value return. 이 경우 궁극적으로 exec_c::exec에서 uop→m_done_cycle을 current core cycle+max latency로 설정하게 되어 나중에 retire stage에서 해당 request를 retire 시킴.
Cache miss: new memory request for MSHR access. 이 때 new_mem_req을 발행하게 되는데, new_mem_req 함수를 보면 맨 마지막에 m_l2_cache[core_id]→insert(new_req)가 있음. 이 함수는 m_in_queue에 cache miss request를 넣는 역할을 함. Cache miss가 확정될 때 schedule_c::uop_schedule( )는 false return을 하고, 이후 cycle에 계속 반복적으로 schedule_c::uop_schedule( )를 통해 cache를 확인.
[Allocate stage] m_allocate→run_a_cycle
[Frontend stage] m_frontend→run_a_cycle

Class별 description

MMU

class PageDescriptor
struct PageDescriptor에 대한 constructor, destructor function을 포함하고 있는 class
struct PageDescriptor는 Addr frame_number, int pid를 변수로 갖고 있음
void MMU::do_page_table_walks(uop_c *cur_uop)
cur_uop에서 pid를 뽑아와서 int pid에 저장
addr, page_number, page_offset에 각각 cur_uop의 m_vaddr, page_number, page_offset 저장
i) 만약 page table hit일 경우
physical address인 cur_uop의 m_paddr에 (frame_number << m_offset_bits) | page_offset 저장
ii) 만약 page fault가 날 경우
FaultInfo new_fault(page_number, pid) 생성
현재 cur_uop->core_id에 해당하는 core pointer를 core_c *core에 저장
cur_uop->m_parent_uop가 있고, page fault buffer가 다 찼다면, cur_uop의 m_state를 OS_TRANS_FAULT_RETRY_QUEUE로 바꾸고, m_fault_retry_queue.emplace_back(cur_uop)로 추가. 그리고 함수 나가기.
fault request인 new_fault를 fault_buffer에 추가
void MMU::handle_page_faults()
batch processing 진행 중이라면(m_batch_processing==TRUE), ended=do_batch_processing(), 그리고 ended가 false라면 함수 종료.
이전 batch processing에서 page fault가 발생하지 않았다면(m_fault_buffer.empty()==TRUE), return. 이전 batch processing에서 page fault가 발생했다면, 다음 batch processing 시작(begin_batch_processing())
int MMU::get_pid(uop_c *cur_uop)
i) 만약 *m_simBase->m_knobs->KNOB_FOOL_PAGE_TABLE==TRUE
return -1
ii) 만약 위의 조건이 아닐 경우
pid에다가 cur_uop->m_core_id에 해당하는 core로부터 get_appl_id(cur_uop->m_thread_id)로 얻어온 application id를 저장
return pid

Want to print your doc?
This is not the way.
Try clicking the ⋯ next to your doc name or using a keyboard shortcut (
CtrlP
) instead.