1 // See LICENSE for license details.
10 #include "processor.h"
11 #include "memtracer.h"
14 // virtual memory configuration
16 const reg_t LEVELS
= sizeof(pte_t
) == 8 ? 3 : 2;
17 const reg_t PTIDXBITS
= 10;
18 const reg_t PGSHIFT
= PTIDXBITS
+ (sizeof(pte_t
) == 8 ? 3 : 2);
19 const reg_t PGSIZE
= 1 << PGSHIFT
;
20 const reg_t VPN_BITS
= PTIDXBITS
* LEVELS
;
21 const reg_t PPN_BITS
= 8*sizeof(reg_t
) - PGSHIFT
;
22 const reg_t VA_BITS
= VPN_BITS
+ PGSHIFT
;
24 // this class implements a processor's port into the virtual memory system.
25 // an MMU and instruction cache are maintained for simulator performance.
29 mmu_t(char* _mem
, size_t _memsz
);
32 // template for functions that load an aligned value from memory
33 #define load_func(type) \
34 type##_t load_##type(reg_t addr) __attribute__((always_inline)) { \
35 if(unlikely(addr % sizeof(type##_t))) \
36 throw trap_load_address_misaligned(addr); \
37 void* paddr = translate(addr, sizeof(type##_t), false, false); \
38 return *(type##_t*)paddr; \
41 // load value from memory at aligned address; zero extend to register width
47 // load value from memory at aligned address; sign extend to register width
53 // template for functions that store an aligned value to memory
54 #define store_func(type) \
55 void store_##type(reg_t addr, type##_t val) { \
56 if(unlikely(addr % sizeof(type##_t))) \
57 throw trap_store_address_misaligned(addr); \
58 void* paddr = translate(addr, sizeof(type##_t), true, false); \
59 *(type##_t*)paddr = val; \
62 // store value to memory at aligned address
77 // load instruction from memory at aligned address.
78 inline insn_fetch_t
load_insn(reg_t addr
)
80 reg_t offset
= addr
& (sizeof(insn_t
) * (ICACHE_ENTRIES
-1));
81 offset
*= sizeof(icache_entry_t
) / sizeof(insn_t
);
82 icache_entry_t
* entry
= (icache_entry_t
*)((char*)icache
+ offset
);
83 insn_fetch_t data
= entry
->data
;
84 if (likely(entry
->tag
== addr
))
87 void* iaddr
= translate(addr
, sizeof(insn_t
), false, true);
89 fetch
.insn
.pad
= *(decltype(fetch
.insn
.insn
.bits())*)iaddr
;
90 fetch
.func
= proc
->decode_insn(fetch
.insn
.insn
);
95 reg_t paddr
= (char*)iaddr
- mem
;
96 if (!tracer
.empty() && tracer
.interested_in_range(paddr
, paddr
+ sizeof(insn_t
), false, true))
99 tracer
.trace(paddr
, sizeof(insn_t
), false, true);
104 void set_processor(processor_t
* p
) { proc
= p
; flush_tlb(); }
109 void register_memtracer(memtracer_t
*);
115 memtracer_list_t tracer
;
117 // implement an instruction cache for simulator performance
118 static const reg_t ICACHE_ENTRIES
= 2048;
119 struct icache_entry_t
{
124 icache_entry_t icache
[ICACHE_ENTRIES
];
126 // implement a TLB for simulator performance
127 static const reg_t TLB_ENTRIES
= 256;
128 char* tlb_data
[TLB_ENTRIES
];
129 reg_t tlb_insn_tag
[TLB_ENTRIES
];
130 reg_t tlb_load_tag
[TLB_ENTRIES
];
131 reg_t tlb_store_tag
[TLB_ENTRIES
];
133 // finish translation on a TLB miss and upate the TLB
134 void* refill_tlb(reg_t addr
, reg_t bytes
, bool store
, bool fetch
);
136 // perform a page table walk for a given virtual address
137 pte_t
walk(reg_t addr
);
139 // translate a virtual address to a physical address
140 void* translate(reg_t addr
, reg_t bytes
, bool store
, bool fetch
)
141 __attribute__((always_inline
))
143 reg_t idx
= (addr
>> PGSHIFT
) % TLB_ENTRIES
;
144 reg_t expected_tag
= addr
& ~(PGSIZE
-1);
146 reg_t
* tlb_tag
= fetch
? tlb_insn_tag
: store
? tlb_store_tag
:tlb_load_tag
;
147 void* data
= tlb_data
[idx
] + addr
;
148 if (likely(tlb_tag
[idx
] == expected_tag
))
151 return refill_tlb(addr
, bytes
, store
, fetch
);
154 friend class processor_t
;