Skip to content

Commit fde15bb

Browse files
committed
Rewrite memory system, move RAM to 0x80000000, fall back to slow instruction fetch/decode if not in icache range
1 parent c2c2049 commit fde15bb

File tree

13 files changed

+343
-323
lines changed

13 files changed

+343
-323
lines changed

README.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,19 @@ RISC-V processor in Mindustry logic. Requires Mindustry build 149+.
66

77
## Architecture
88

9-
Memory consists of three sections. Two are directly accessible by code: ROM (rx) and RAM (rw). The third section is an instruction cache, which takes up 4x as much space as the executable portion of memory. The instruction cache is updated at reset and whenever an instruction writes to RAM.
9+
Physical memory consists of three sections. Two are directly accessible by code: ROM (rx) and RAM (rwx). The third section is an instruction cache which is 4x less dense than main memory. The instruction cache is updated at reset, and whenever an instruction writes to RAM that is covered by the icache. If executing from memory not covered by the icache, the processor manually fetches and decodes the instruction from main memory.
1010

11-
Code begins executing at address `0x4`. Address `0x0` must contain the size of the `.text` section (ie. `__etext`) to tell the processor how much data to decode from ROM; alternatively, it can be `0` to decode the entire ROM.
11+
Code begins executing at address `0x4`. Address `0x0` must contain the size of the `.text` section (ie. `__etext`) to tell the processor how much data to decode from ROM. If this value is `0`, no ROM data will be decoded.
1212

1313
The main CPU code is generated from `src/main.mlog.jinja` using a custom Jinja-based preprocessor (`python/src/mlogv32/preprocessor`).
1414

15-
### MMIO
15+
## Memory
16+
17+
| Address | Value |
18+
| ------------ | ----- |
19+
| `0x00000000` | ROM |
20+
| `0x80000000` | RAM |
21+
| `0xf0000000` | MMIO |
1622

1723
Addresses `0xf0000000` - `0xffffffff` are reserved for MMIO.
1824

coremark/mlogv32/core_portme.mak

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ SEPARATE_COMPILE=1
4343
# You must also define below how to create an object file, and how to link.
4444
OBJOUT = -o
4545
LFLAGS = -T$(PORT_DIR)/../../rust/mlogv32/link.x
46-
ASFLAGS = --compile
46+
ASFLAGS = --compile -march=$(RISCV_ARCH)
4747
OFLAG = -o
4848
COUT = -c
4949

coremark/mlogv32/entry.s

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22

33
.global _start
44
_start:
5+
# reset mtime/mcycle/minstret
6+
li t0, 0xf0000000 # mtime
7+
sw zero, 4(t0)
8+
sw zero, 0(t0)
9+
10+
csrw mcycleh, zero
11+
csrw mcycle, zero
12+
13+
csrw minstreth, zero
14+
csrw minstret, zero
15+
516
la t1, _stack_start
617
andi sp, t1, -16
718
add s0, sp, zero

mod/src/main/kotlin/gay/object/mlogv32/Mlogv32UtilsMod.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,14 @@ class Mlogv32UtilsMod : Mod() {
6565
file.parent().mkdirs()
6666

6767
val startAddress = if (args.size >= 4) {
68-
val result = args[3].toIntOrNull()
68+
val result = args[3].toUIntOrNull()
6969
if (result == null) {
7070
Log.err("Failed to parse start address.")
7171
return@register
7272
}
7373
result
7474
} else {
75-
processor.ramStart
75+
ProcessorAccess.RAM_START
7676
}
7777

7878
val bytes = if (args.size >= 5) {
@@ -83,7 +83,7 @@ class Mlogv32UtilsMod : Mod() {
8383
}
8484
result
8585
} else {
86-
processor.ramEnd - startAddress
86+
(processor.ramEnd - startAddress).toInt()
8787
}
8888

8989
try {

mod/src/main/kotlin/gay/object/mlogv32/ProcessorAccess.kt

Lines changed: 53 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -25,33 +25,30 @@ class ProcessorAccess(
2525
val memoryX: Int,
2626
val memoryY: Int,
2727
val memoryWidth: Int,
28-
val romByteOffset: Int,
29-
val romProcSize: Int,
30-
val romStart: Int,
31-
val romEnd: Int,
32-
val ramProcSize: Int,
33-
val ramStart: Int,
34-
val ramEnd: Int,
35-
val resetSwitch: SwitchBuild,
28+
val romSize: Int,
29+
val ramSize: Int,
3630
val errorOutput: MessageBuild,
31+
val resetSwitch: SwitchBuild,
3732
) {
38-
val romSize = romEnd - romStart
39-
val ramSize = ramEnd - ramStart
33+
val romEnd = ROM_START + romSize.toUInt()
34+
val ramEnd = RAM_START + ramSize.toUInt()
35+
36+
val ramStartProc = (romEnd / ROM_PROC_BYTES.toUInt()).toInt()
4037

4138
fun flashRom(file: Fi): Int {
4239
val data = file.readBytes()
4340
require(data.size.mod(4) == 0) { "Data length must be a multiple of 4 bytes." }
4441
require(data.size <= romSize) { "Data is too large to fit into the processor's ROM." }
4542

46-
var address = romStart
43+
var address = ROM_START
4744
val bytes = mutableListOf<Byte>()
4845
for (byte in data) {
4946
bytes.add(byte)
5047

51-
if (bytes.size == romProcSize) {
48+
if (bytes.size == ROM_PROC_BYTES) {
5249
flashRomProc(address, bytes)
5350
bytes.clear()
54-
address += romProcSize
51+
address += ROM_PROC_BYTES.toUInt()
5552
}
5653
}
5754

@@ -64,11 +61,11 @@ class ProcessorAccess(
6461
}
6562

6663
fun dumpRam(file: Fi): Int {
67-
dumpRam(file, ramStart, ramSize)
64+
dumpRam(file, RAM_START, ramSize)
6865
return ramSize
6966
}
7067

71-
fun dumpRam(file: Fi, startAddress: Int, bytes: Int) {
68+
fun dumpRam(file: Fi, startAddress: UInt, bytes: Int) {
7269
require(bytes > 0) { "Bytes must be positive." }
7370
require(bytes <= ramSize) { "Bytes must not be greater than the RAM size." }
7471
require(bytes.mod(4) == 0) { "Bytes must be aligned to 4 bytes." }
@@ -119,25 +116,24 @@ class ProcessorAccess(
119116
ProcessorAccess.stopServer()
120117
}
121118

122-
private fun ramWordsSequence(startAddress: Int = 0) = sequence {
123-
require(startAddress in ramStart..<ramEnd) { "Start address must be within RAM." }
124-
require(startAddress.mod(4) == 0) { "Start address must be aligned to 4 bytes." }
119+
private fun ramWordsSequence(startAddress: UInt) = sequence {
120+
require(startAddress in RAM_START..<ramEnd) { "Start address must be within RAM." }
121+
require(startAddress.mod(4u) == 0u) { "Start address must be aligned to 4 bytes." }
125122

126123
var address = startAddress
127124
while (address < ramEnd) {
128-
val proc = getRamProc(address) ?: break
129-
val startIndex = (address / 4).mod(ramProcSize) + 1
130-
for (i in startIndex..ramProcSize) {
131-
yield(proc.executor.vars[i]!! to address)
132-
address += 4
125+
val (proc, startIndex) = getRamProc(address) ?: break
126+
for (i in startIndex..<RAM_PROC_VARS) {
127+
yield(proc.executor.vars[i + 1]!! to address)
128+
address += 4u
133129
}
134130
}
135131
}
136132

137-
private fun getRomProc(address: Int): LogicBuild? {
138-
if (address !in romStart..<romEnd) return null
133+
private fun getRomProc(address: UInt): LogicBuild? {
134+
if (address !in ROM_START..<romEnd) return null
139135

140-
val index = address / romProcSize
136+
val index = (address / ROM_PROC_BYTES.toUInt()).toInt()
141137
val x = memoryX + index.mod(memoryWidth)
142138
val y = memoryY + index / memoryWidth
143139

@@ -148,11 +144,11 @@ class ProcessorAccess(
148144
return proc
149145
}
150146

151-
private fun flashRomProc(address: Int, data: Iterable<Byte>) {
147+
private fun flashRomProc(address: UInt, data: Iterable<Byte>) {
152148
val code = buildString {
153149
append("set v \"")
154150
for (byte in data) {
155-
val char = (byte.toInt() and 0xff) + romByteOffset
151+
val char = (byte.toInt() and 0xff) + ROM_BYTE_OFFSET
156152
append(char.toChar())
157153
}
158154
append("\"\nstop")
@@ -164,22 +160,25 @@ class ProcessorAccess(
164160
proc.updateCode(code)
165161
}
166162

167-
private fun getRamProc(address: Int): LogicBuild? {
168-
if (address !in ramStart..<ramEnd) return null
163+
private fun getRamProc(address: UInt): Pair<LogicBuild, Int>? {
164+
if (address !in RAM_START..<ramEnd) return null
165+
166+
val ramAddress = address - RAM_START
167+
val ramVariable = (ramAddress / 4u).toInt()
169168

170169
// each ROM and RAM proc currently holds the same amount of data, so this doesn't matter too much
171-
val index = address / 4 / ramProcSize
170+
val index = ramStartProc + ramVariable / RAM_PROC_VARS
172171
val x = memoryX + index.mod(memoryWidth)
173172
val y = memoryY + index / memoryWidth
174173

175174
val proc = Vars.world.build(x, y) as? LogicBuild ?: return null
176175

177176
if (
178-
proc.executor.vars.size != ramProcSize + 1
177+
proc.executor.vars.size != RAM_PROC_VARS + 1
179178
|| proc.executor.vars[1].name != "!!"
180179
) return null
181180

182-
return proc
181+
return proc to ramVariable.mod(RAM_PROC_VARS)
183182
}
184183

185184
private suspend fun runServer(serverSocket: ServerSocket) {
@@ -212,6 +211,13 @@ class ProcessorAccess(
212211
}
213212

214213
companion object {
214+
const val ROM_BYTE_OFFSET = 174
215+
const val ROM_PROC_BYTES = 16384
216+
const val RAM_PROC_VARS = 4096
217+
218+
const val ROM_START = 0x00000000u
219+
const val RAM_START = 0x80000000u
220+
215221
private var serverThread: Thread? = null
216222
private var serverJob: Job? = null
217223
private var serverBuildId: Int? = null
@@ -222,15 +228,10 @@ class ProcessorAccess(
222228
memoryX = nonZeroIntVar(build, "MEMORY_X") ?: return null,
223229
memoryY = nonZeroIntVar(build, "MEMORY_Y") ?: return null,
224230
memoryWidth = positiveIntVar(build, "MEMORY_WIDTH") ?: return null,
225-
romByteOffset = nonNegativeIntVar(build, "ROM_BYTE_OFFSET") ?: return null,
226-
romProcSize = positiveIntVar(build, "ROM_PROC_SIZE") ?: return null,
227-
romStart = nonNegativeIntVar(build, "ROM_START") ?: return null,
228-
romEnd = nonNegativeIntVar(build, "ROM_END") ?: return null,
229-
ramProcSize = positiveIntVar(build, "RAM_PROC_SIZE") ?: return null,
230-
ramStart = nonNegativeIntVar(build, "RAM_START") ?: return null,
231-
ramEnd = nonNegativeIntVar(build, "RAM_END") ?: return null,
232-
resetSwitch = buildVar<SwitchBuild>(build, "RESET_SWITCH") ?: return null,
233-
errorOutput = buildVar<MessageBuild>(build, "ERROR_OUTPUT") ?: return null,
231+
romSize = positiveIntVar(build, "ROM_SIZE") ?: return null,
232+
ramSize = positiveIntVar(build, "RAM_SIZE") ?: return null,
233+
errorOutput = buildVar<MessageBuild>(build, "message1") ?: return null,
234+
resetSwitch = buildVar<SwitchBuild>(build, "switch1") ?: return null,
234235
)
235236
}
236237

@@ -250,18 +251,18 @@ private fun nonZeroIntVar(build: LogicBuild, name: String): Int? =
250251
?.numi()
251252
?.takeIf { it != 0 }
252253

253-
private fun nonNegativeIntVar(build: LogicBuild, name: String): Int? =
254-
build.executor.optionalVar(name)
255-
?.takeIf { !it.isobj }
256-
?.numi()
257-
?.takeIf { it >= 0 }
258-
259254
private fun positiveIntVar(build: LogicBuild, name: String): Int? =
260255
nonZeroIntVar(build, name)
261256
?.takeIf { it > 0 }
262257

263258
private inline fun <reified T : Building> buildVar(build: LogicBuild, name: String): T? =
264-
build.executor.optionalVar(name)?.obj() as? T
259+
(build.executor.optionalVar(name)?.obj() ?: linkedBuild(build, name)) as? T
260+
261+
// why
262+
private fun linkedBuild(build: LogicBuild, name: String) =
263+
build.links
264+
.firstOrNull { it.active && it.valid && it.name == name }
265+
?.let { Vars.world.build(it.x, it.y) }
265266

266267
@Serializable
267268
sealed class Request {
@@ -296,15 +297,15 @@ data class FlashRequest(val path: String) : Request() {
296297
@SerialName("dump")
297298
data class DumpRequest(
298299
val path: String,
299-
val address: Int?,
300+
val address: UInt?,
300301
val bytes: Int?,
301302
) : Request() {
302303
override suspend fun handle(processor: ProcessorAccess) = runOnMainThread {
303304
val file = Core.files.absolute(path)
304305
file.parent().mkdirs()
305306

306-
val address = address ?: processor.ramStart
307-
val bytes = bytes ?: (processor.ramEnd - address)
307+
val address = address ?: ProcessorAccess.RAM_START
308+
val bytes = bytes ?: (processor.ramEnd - address).toInt()
308309

309310
processor.dumpRam(file, address, bytes)
310311
SuccessResponse("Successfully dumped $bytes bytes from RAM to $file.")

riscof/mlogv32/env/link.ld

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ OUTPUT_ARCH( "riscv" )
22
ENTRY(rvtest_entry_point)
33

44
MEMORY {
5-
ROM (rx) : ORIGIN = 0, LENGTH = 0x200000
6-
RAM (rw) : ORIGIN = 0x200000, LENGTH = 0x200000
5+
ROM (rx) : ORIGIN = 0x00000000, LENGTH = 0x200000
6+
RAM (rwx) : ORIGIN = 0x80000000, LENGTH = 0x200000
7+
MMIO (rw) : ORIGIN = 0xf0000000, LENGTH = 16
78
}
89

910
SECTIONS

rust/mlogv32/link.x

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,17 @@ MEMORY {
1212
0x3000000 = 128*24 * 4096*4
1313
0x1000000 = 128*8 * 4096*4
1414
*/
15-
rom (rx) : ORIGIN = 0, LENGTH = 0x200000
16-
ram (rw) : ORIGIN = 0x200000, LENGTH = 0x200000
15+
ROM (rx) : ORIGIN = 0x00000000, LENGTH = 0x200000
16+
RAM (rwx) : ORIGIN = 0x80000000, LENGTH = 0x200000
17+
MMIO (rw) : ORIGIN = 0xf0000000, LENGTH = 16
1718
}
1819

19-
REGION_ALIAS("REGION_TEXT", rom);
20-
REGION_ALIAS("REGION_RODATA", rom);
21-
REGION_ALIAS("REGION_DATA", ram);
22-
REGION_ALIAS("REGION_BSS", ram);
23-
REGION_ALIAS("REGION_HEAP", ram);
24-
REGION_ALIAS("REGION_STACK", ram);
20+
REGION_ALIAS("REGION_TEXT", ROM);
21+
REGION_ALIAS("REGION_RODATA", ROM);
22+
REGION_ALIAS("REGION_DATA", RAM);
23+
REGION_ALIAS("REGION_BSS", RAM);
24+
REGION_ALIAS("REGION_HEAP", RAM);
25+
REGION_ALIAS("REGION_STACK", RAM);
2526

2627
PROVIDE(_stack_start = ORIGIN(REGION_STACK) + LENGTH(REGION_STACK));
2728
PROVIDE(_stack_size = 4K);

src/config.mlog

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
# per-world config options
2-
set IPT 500000
3-
set MEMORY_X_OFFSET -3 # x offset from cpu of bottom left memory proc
4-
set MEMORY_Y_OFFSET -130 # y offset from cpu of bottom left memory proc
5-
set MEMORY_WIDTH 128 # physical width of the memory procs
6-
set ROM_SIZE 0x1000000 # total rom size in bytes (rx); also total decoded size in variables
7-
set RAM_SIZE 0x3000000 # total ram size in bytes (rw)
2+
set TARGET_IPT 500000
3+
4+
set MEMORY_X_OFFSET -11 # x offset from this proc to bottom left memory proc
5+
set MEMORY_Y_OFFSET -26 # y offset from this proc to bottom left memory proc
6+
set MEMORY_WIDTH 32 # physical width of the memory procs
7+
8+
set ROM_SIZE 0x200000 # ROM size in bytes (rx)
9+
set RAM_SIZE 0x200000 # RAM size in bytes (rwx)
10+
set ICACHE_SIZE 0x200000 # icache size in variables, or bytes of memory it can represent; 4x less dense than ROM/RAM
11+
12+
# computed values
13+
op add MEMORY_X @thisx MEMORY_X_OFFSET
14+
op add MEMORY_Y @thisy MEMORY_Y_OFFSET
15+
816
stop

0 commit comments

Comments
 (0)