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
;
30 struct icache_entry_t
{
36 // this class implements a processor's port into the virtual memory system.
37 // an MMU and instruction cache are maintained for simulator performance.
41 mmu_t(char* _mem
, size_t _memsz
);
44 // template for functions that load an aligned value from memory
45 #define load_func(type) \
46 type##_t load_##type(reg_t addr) __attribute__((always_inline)) { \
47 void* paddr = translate(addr, sizeof(type##_t), false, false); \
48 return *(type##_t*)paddr; \
51 // load value from memory at aligned address; zero extend to register width
57 // load value from memory at aligned address; sign extend to register width
63 // template for functions that store an aligned value to memory
64 #define store_func(type) \
65 void store_##type(reg_t addr, type##_t val) { \
66 void* paddr = translate(addr, sizeof(type##_t), true, false); \
67 *(type##_t*)paddr = val; \
70 // store value to memory at aligned address
76 static const reg_t ICACHE_ENTRIES
= 1024;
78 inline size_t icache_index(reg_t addr
)
80 // for instruction sizes != 4, this hash still works but is suboptimal
81 return (addr
/ 4) % ICACHE_ENTRIES
;
84 // load instruction from memory at aligned address.
85 icache_entry_t
* access_icache(reg_t addr
) __attribute__((always_inline
))
87 reg_t idx
= icache_index(addr
);
88 icache_entry_t
* entry
= &icache
[idx
];
89 if (likely(entry
->tag
== addr
))
92 bool rvc
= false; // set this dynamically once RVC is re-implemented
93 char* iaddr
= (char*)translate(addr
, rvc
? 2 : 4, false, true);
94 insn_bits_t insn
= *(uint16_t*)iaddr
;
96 if (unlikely(insn_length(insn
) == 2)) {
98 } else if (likely(insn_length(insn
) == 4)) {
99 if (likely((addr
& (PGSIZE
-1)) < PGSIZE
-2))
100 insn
|= (insn_bits_t
)*(int16_t*)(iaddr
+ 2) << 16;
102 insn
|= (insn_bits_t
)*(int16_t*)translate(addr
+ 2, 2, false, true) << 16;
103 } else if (insn_length(insn
) == 6) {
104 insn
|= (insn_bits_t
)*(int16_t*)translate(addr
+ 4, 2, false, true) << 32;
105 insn
|= (insn_bits_t
)*(uint16_t*)translate(addr
+ 2, 2, false, true) << 16;
107 static_assert(sizeof(insn_bits_t
) == 8, "insn_bits_t must be uint64_t");
108 insn
|= (insn_bits_t
)*(int16_t*)translate(addr
+ 6, 2, false, true) << 48;
109 insn
|= (insn_bits_t
)*(uint16_t*)translate(addr
+ 4, 2, false, true) << 32;
110 insn
|= (insn_bits_t
)*(uint16_t*)translate(addr
+ 2, 2, false, true) << 16;
113 insn_fetch_t fetch
= {proc
->decode_insn(insn
), insn
};
114 icache
[idx
].tag
= addr
;
115 icache
[idx
].data
= fetch
;
117 reg_t paddr
= iaddr
- mem
;
118 if (!tracer
.empty() && tracer
.interested_in_range(paddr
, paddr
+ 1, false, true))
120 icache
[idx
].tag
= -1;
121 tracer
.trace(paddr
, 1, false, true);
126 inline insn_fetch_t
load_insn(reg_t addr
)
128 return access_icache(addr
)->data
;
131 void set_processor(processor_t
* p
) { proc
= p
; flush_tlb(); }
136 void register_memtracer(memtracer_t
*);
142 memtracer_list_t tracer
;
144 // implement an instruction cache for simulator performance
145 icache_entry_t icache
[ICACHE_ENTRIES
];
147 // implement a TLB for simulator performance
148 static const reg_t TLB_ENTRIES
= 256;
149 char* tlb_data
[TLB_ENTRIES
];
150 reg_t tlb_insn_tag
[TLB_ENTRIES
];
151 reg_t tlb_load_tag
[TLB_ENTRIES
];
152 reg_t tlb_store_tag
[TLB_ENTRIES
];
154 // finish translation on a TLB miss and upate the TLB
155 void* refill_tlb(reg_t addr
, reg_t bytes
, bool store
, bool fetch
);
157 // perform a page table walk for a given VA; set referenced/dirty bits
158 pte_t
walk(reg_t addr
, reg_t perm
);
160 // translate a virtual address to a physical address
161 void* translate(reg_t addr
, reg_t bytes
, bool store
, bool fetch
)
162 __attribute__((always_inline
))
164 reg_t idx
= (addr
>> PGSHIFT
) % TLB_ENTRIES
;
165 reg_t expected_tag
= addr
>> PGSHIFT
;
166 reg_t
* tags
= fetch
? tlb_insn_tag
: store
? tlb_store_tag
:tlb_load_tag
;
167 reg_t tag
= tags
[idx
];
168 void* data
= tlb_data
[idx
] + addr
;
170 if (unlikely(addr
& (bytes
-1)))
171 store
? throw trap_store_address_misaligned(addr
) :
172 fetch
? throw trap_instruction_address_misaligned(addr
) :
173 throw trap_load_address_misaligned(addr
);
175 if (likely(tag
== expected_tag
))
178 return refill_tlb(addr
, bytes
, store
, fetch
);
181 friend class processor_t
;