Initial commit
This commit is contained in:
parent
522c95ea0a
commit
866eebd43d
16
Makefile
16
Makefile
@ -9,13 +9,25 @@ SRC := $(shell find . -type f -name '*.go' -print) go.mod go.sum
|
|||||||
all: build
|
all: build
|
||||||
|
|
||||||
.PHONY: build
|
.PHONY: build
|
||||||
build: build-cpusim
|
build: build-cpusim build-asmtest build-simtest
|
||||||
|
|
||||||
.PHONY: build-cpusim
|
.PHONY: build-cpusim
|
||||||
build-cpusim: $(BINDIR)/cpusim
|
build-cpusim: $(BINDIR)/cpusim
|
||||||
|
|
||||||
$(BINDIR)/cpusim: $(SRC)
|
$(BINDIR)/cpusim: $(SRC)
|
||||||
go build -trimpath -o $@ ./cmd/cpusim
|
go build -trimpath -o $@ ./cmd/$(@F)
|
||||||
|
|
||||||
|
.PHONY: build-asmtest
|
||||||
|
build-asmtest: $(BINDIR)/asmtest
|
||||||
|
|
||||||
|
$(BINDIR)/asmtest: $(SRC)
|
||||||
|
go build -trimpath -o $@ ./cmd/$(@F)
|
||||||
|
|
||||||
|
.PHONY: build-simtest
|
||||||
|
build-simtest: $(BINDIR)/simtest
|
||||||
|
|
||||||
|
$(BINDIR)/simtest: $(SRC)
|
||||||
|
go build -trimpath -o $@ ./cmd/$(@F)
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
|
27
cmd/asmtest/asmtest.go
Normal file
27
cmd/asmtest/asmtest.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/cpu"
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/instr"
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/simcontext"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MOV_r1_r2 instr.Instr = 0xffff_ff2a
|
||||||
|
SVC_ffffff instr.Instr = 0xffff_ffff
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cpu := cpu.New(simcontext.SimContext{}, nil)
|
||||||
|
cpu.RegisterInstr(instr.InstrDesc{
|
||||||
|
Mnemonic: "LDR",
|
||||||
|
VariantName: "LDRIR",
|
||||||
|
OpCode: 0x01,
|
||||||
|
Format: instr.A,
|
||||||
|
Formatter: func(i *instr.DecodedInstr) string { return "Hello" }})
|
||||||
|
|
||||||
|
fmt.Printf("MOV[6:0] = %#0x\n", cpu.GetOpCode(MOV_r1_r2))
|
||||||
|
fmt.Printf("SVC[6:0] = %#0x\n", cpu.GetOpCode(SVC_ffffff))
|
||||||
|
}
|
48
cmd/simtest/main.go
Normal file
48
cmd/simtest/main.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/breakcfg"
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/events"
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/instrset"
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/sim"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SysoutEventLogger struct{}
|
||||||
|
|
||||||
|
func (l *SysoutEventLogger) Log(e events.SimEvent) {
|
||||||
|
log.Printf("%v\n", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
eventLogger := &SysoutEventLogger{}
|
||||||
|
|
||||||
|
simConfig := sim.SimConfig{
|
||||||
|
RamSize: 64,
|
||||||
|
UseMMU: false,
|
||||||
|
UseCache: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
schedulerConfig := sim.SchedulerConfig{BreakableEventConfig: breakcfg.New()}
|
||||||
|
breakcfg.SetSteppingType[events.CpuDecodedEvent](schedulerConfig.BreakableEventConfig, breakcfg.ALLOW_BREAK)
|
||||||
|
|
||||||
|
sim := sim.New(simConfig, schedulerConfig, eventLogger)
|
||||||
|
|
||||||
|
sim.RegisterInstructionSet(instrset.CentralInstructionSet)
|
||||||
|
|
||||||
|
/*
|
||||||
|
sim.QueueStateAccess(func(s simstate.SimState) {
|
||||||
|
s.RamState.Data[0] = 0xdeaddead
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
sim.Start()
|
||||||
|
}()
|
||||||
|
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
sim.Stop()
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
}
|
2
go.mod
2
go.mod
@ -1,4 +1,4 @@
|
|||||||
module git.elyanpoujol.fr/elyan/cpu
|
module git.elyanpoujol.fr/elyan/central-arch
|
||||||
|
|
||||||
go 1.23.2
|
go 1.23.2
|
||||||
|
|
||||||
|
91
pkg/breakcfg/breakcfg.go
Normal file
91
pkg/breakcfg/breakcfg.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package breakcfg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/events"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BreakableEventId uint
|
||||||
|
|
||||||
|
/*
|
||||||
|
const (
|
||||||
|
// CPU events
|
||||||
|
CPU_FETCH_INSTR_BEGIN BreakableEventId = iota
|
||||||
|
CPU_FETCH_INSTR_END
|
||||||
|
CPU_DECODED_INSTR
|
||||||
|
CPU_EXECUTED_INSTR
|
||||||
|
CPU_READ_MEM_REQUEST
|
||||||
|
CPU_WRITE_MEM_REQUEST
|
||||||
|
CPU_MODE_TRANSITION
|
||||||
|
// MMU events
|
||||||
|
MMU_TLB_NEXT_ENTRY
|
||||||
|
MMU_TLB_HIT
|
||||||
|
MMU_TLB_MISS
|
||||||
|
MMU_TLB_EVICT_ENTRY
|
||||||
|
MMU_TLB_NEW_ENTRY
|
||||||
|
MMU_WALK_NEXT_ENTRY
|
||||||
|
MMU_WALK_NEXT_LEVEL
|
||||||
|
MMU_WALK_HIT
|
||||||
|
MMU_WALK_MISS
|
||||||
|
// Cache events
|
||||||
|
CACHE_STEP_NEXT_ENTRY
|
||||||
|
CACHE_STEP_HIT
|
||||||
|
CACHE_STEP_MISS
|
||||||
|
CACHE_EVICT_ENTRY
|
||||||
|
CACHE_NEW_ENTRY
|
||||||
|
// Memory events
|
||||||
|
MEMORY_READ
|
||||||
|
MEMORY_WRITE
|
||||||
|
MEMORY_UNALIGNED_READ
|
||||||
|
MEMORY_UNALIGNED_WRITE
|
||||||
|
// Event count, not a real event
|
||||||
|
BREAKABLE_EVENT_ID_COUNT
|
||||||
|
)
|
||||||
|
*/
|
||||||
|
|
||||||
|
type SteppingType uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
ALLOW_BREAK SteppingType = iota // Default value, allow breakpoints for a given event type
|
||||||
|
ALWAYS_BREAK // Always break even if no breakpoints were set for the event type
|
||||||
|
IGNORE_BREAK // Ignore the breakpoints for the event type
|
||||||
|
)
|
||||||
|
|
||||||
|
type BreakableEventConfig struct {
|
||||||
|
eventSteppingTypes map[reflect.Type]SteppingType
|
||||||
|
}
|
||||||
|
|
||||||
|
func New() *BreakableEventConfig {
|
||||||
|
cfg := &BreakableEventConfig{
|
||||||
|
eventSteppingTypes: make(map[reflect.Type]SteppingType),
|
||||||
|
}
|
||||||
|
cfg.init()
|
||||||
|
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetSteppingType[T events.SimEvent](cfg *BreakableEventConfig, st SteppingType) {
|
||||||
|
cfg.eventSteppingTypes[key[T]()] = st
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSteppingType[T events.SimEvent](cfg *BreakableEventConfig) SteppingType {
|
||||||
|
return cfg.eventSteppingTypes[key[T]()]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *BreakableEventConfig) init() {
|
||||||
|
// TODO Add all breakable events
|
||||||
|
SetSteppingType[events.CpuFetchBeginEvent](cfg, ALLOW_BREAK)
|
||||||
|
SetSteppingType[events.CpuFetchEndEvent](cfg, ALLOW_BREAK)
|
||||||
|
SetSteppingType[events.CpuDecodedEvent](cfg, ALLOW_BREAK)
|
||||||
|
SetSteppingType[events.CpuExecuteEvent](cfg, ALLOW_BREAK)
|
||||||
|
SetSteppingType[events.CpuReadMemoryBeginEvent](cfg, ALLOW_BREAK)
|
||||||
|
SetSteppingType[events.CpuReadMemoryEndEvent](cfg, ALLOW_BREAK)
|
||||||
|
SetSteppingType[events.CpuWriteMemoryBeginEvent](cfg, ALLOW_BREAK)
|
||||||
|
SetSteppingType[events.CpuWriteMemoryEndEvent](cfg, ALLOW_BREAK)
|
||||||
|
SetSteppingType[events.CpuModeTransitionEvent](cfg, ALLOW_BREAK)
|
||||||
|
}
|
||||||
|
|
||||||
|
func key[T events.SimEvent]() reflect.Type {
|
||||||
|
return reflect.TypeFor[T]()
|
||||||
|
}
|
25
pkg/cache/cache.go
vendored
Normal file
25
pkg/cache/cache.go
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/memory"
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/simcontext"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO Add a field of type CacheState
|
||||||
|
type Cache struct{}
|
||||||
|
|
||||||
|
func (c *Cache) Read(addr uint32) (uint32, error) {
|
||||||
|
panic("Not implemented yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) Write(addr uint32, value uint32) error {
|
||||||
|
panic("Not implemented yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Cache) Fetch(addr uint32) (uint32, error) {
|
||||||
|
panic("Not implemented yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(context simcontext.SimContext, realMemory memory.Memory) *Cache {
|
||||||
|
panic("Not implemented yet")
|
||||||
|
}
|
28
pkg/cpu/cpu.go
Normal file
28
pkg/cpu/cpu.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package cpu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/events"
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/memory"
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/simcontext"
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/simstate"
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/simstep"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Cpu struct {
|
||||||
|
state simstate.CpuState
|
||||||
|
stepper simstep.SimStepper
|
||||||
|
eventLogger events.EventLogger
|
||||||
|
memory memory.Memory
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(context simcontext.SimContext, memory memory.Memory) *Cpu {
|
||||||
|
return &Cpu{
|
||||||
|
stepper: context.Stepper,
|
||||||
|
eventLogger: context.EventLogger,
|
||||||
|
memory: memory,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cpu) State() *simstate.CpuState {
|
||||||
|
return &c.state
|
||||||
|
}
|
33
pkg/cpu/exec.go
Normal file
33
pkg/cpu/exec.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package cpu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/events"
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/simstep"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (cpu *Cpu) Run(wg *sync.WaitGroup) {
|
||||||
|
wg.Add(1)
|
||||||
|
defer wg.Done()
|
||||||
|
// TODO Implement CPU
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
for err == nil {
|
||||||
|
event := &events.DebugEvent{Msg: "Will step"}
|
||||||
|
|
||||||
|
cpu.eventLogger.Log(event)
|
||||||
|
err = cpu.stepper.RequestStep(event)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if err != simstep.SimQuitRequest {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu.eventLogger.Log(&events.DebugEvent{Msg: "Scheduler quit requested"})
|
||||||
|
} else {
|
||||||
|
cpu.eventLogger.Log(&events.DebugEvent{Msg: "After step"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
pkg/cpu/instr.go
Normal file
26
pkg/cpu/instr.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package cpu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/instr"
|
||||||
|
)
|
||||||
|
|
||||||
|
const instrMask = 0x7f
|
||||||
|
|
||||||
|
func (cpu *Cpu) RegisterInstr(instrDesc instr.InstrDesc) {
|
||||||
|
// TODO Implement it
|
||||||
|
log.Printf("Registered: %+v\n", instrDesc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cpu *Cpu) GetOpCode(i instr.Instr) uint32 {
|
||||||
|
return uint32(i & instrMask)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cpu *Cpu) GetFormat(i instr.Instr) instr.InstrFormat {
|
||||||
|
panic("Not implemented yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cpu *Cpu) Decode(i instr.Instr, decodedInstr *instr.DecodedInstr) {
|
||||||
|
panic("Not implemented yet")
|
||||||
|
}
|
66
pkg/events/events.go
Normal file
66
pkg/events/events.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package events
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/modes"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SimEvent interface{}
|
||||||
|
|
||||||
|
type DebugEvent struct {
|
||||||
|
Msg string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (de *DebugEvent) String() string {
|
||||||
|
return de.Msg
|
||||||
|
}
|
||||||
|
|
||||||
|
type CpuFetchBeginEvent struct {
|
||||||
|
Addr uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type CpuFetchEndEvent struct {
|
||||||
|
Addr uint32
|
||||||
|
Value uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type CpuDecodedEvent struct {
|
||||||
|
Addr uint32
|
||||||
|
InstrValue uint32
|
||||||
|
InstrString string
|
||||||
|
}
|
||||||
|
|
||||||
|
type CpuExecuteEvent struct {
|
||||||
|
Addr uint32
|
||||||
|
InstrValue uint32
|
||||||
|
InstrString string
|
||||||
|
}
|
||||||
|
|
||||||
|
type CpuReadMemoryBeginEvent struct {
|
||||||
|
Addr uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type CpuReadMemoryEndEvent struct {
|
||||||
|
Addr uint32
|
||||||
|
Value uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type CpuWriteMemoryBeginEvent struct {
|
||||||
|
Addr uint32
|
||||||
|
Value uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type CpuWriteMemoryEndEvent struct {
|
||||||
|
Addr uint32
|
||||||
|
Value uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type CpuModeTransitionEvent struct {
|
||||||
|
Prev modes.CpuMode
|
||||||
|
Next modes.CpuMode
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Add more events
|
||||||
|
|
||||||
|
type EventLogger interface {
|
||||||
|
Log(e SimEvent)
|
||||||
|
}
|
101
pkg/instr/instr.go
Normal file
101
pkg/instr/instr.go
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
package instr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/register"
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/simstate"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Instr uint32
|
||||||
|
type InstrFormat uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
A InstrFormat = iota
|
||||||
|
B
|
||||||
|
C
|
||||||
|
D1
|
||||||
|
D2
|
||||||
|
E
|
||||||
|
)
|
||||||
|
|
||||||
|
func (instrf InstrFormat) String() string {
|
||||||
|
switch instrf {
|
||||||
|
case A:
|
||||||
|
return "A"
|
||||||
|
|
||||||
|
case B:
|
||||||
|
return "B"
|
||||||
|
|
||||||
|
case C:
|
||||||
|
return "C"
|
||||||
|
|
||||||
|
case D1:
|
||||||
|
return "D1"
|
||||||
|
|
||||||
|
case D2:
|
||||||
|
return "D2"
|
||||||
|
|
||||||
|
case E:
|
||||||
|
return "E"
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("unknown format")
|
||||||
|
}
|
||||||
|
|
||||||
|
type Cond uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
AL Cond = iota // Always
|
||||||
|
EQ // Equal, Z==1
|
||||||
|
NEQ // Not Equal, Z==0
|
||||||
|
UGE // Unsigned Greater or Equal, C==1
|
||||||
|
CS Cond = iota - 1 // Carry Set, C==1
|
||||||
|
ULT // Unsigned Lower Than, C==0
|
||||||
|
CC Cond = iota - 2 // Carry Clear, C==0
|
||||||
|
NEG // Negative, N==1
|
||||||
|
POS // Positive, N==0
|
||||||
|
VS // oVerflow Set, V==1
|
||||||
|
VC // oVerflow Clear, V==0
|
||||||
|
UGT // Unsigned Greater Than, C==1 && Z==0
|
||||||
|
ULE // Unsigned Lower Than, C==0 && Z==1
|
||||||
|
SGE // Signed Greater or Equal, N==V
|
||||||
|
SLT // Signed Lower Than, N!=V
|
||||||
|
SGT // Signed Greater Than, Z==0 && N==V
|
||||||
|
SLE // Signed Lower or Equal, Z==1 && N!=V
|
||||||
|
)
|
||||||
|
|
||||||
|
type InstrFormatter func(*DecodedInstr) string
|
||||||
|
|
||||||
|
type InstrBehavior func(*DecodedInstr, simstate.SimState) error
|
||||||
|
|
||||||
|
type InstrDesc struct {
|
||||||
|
VariantName string
|
||||||
|
Mnemonic string
|
||||||
|
OpCode uint32
|
||||||
|
Format InstrFormat
|
||||||
|
Formatter InstrFormatter
|
||||||
|
Behavior InstrBehavior
|
||||||
|
}
|
||||||
|
|
||||||
|
type StandardFormatFields struct {
|
||||||
|
Condition Cond
|
||||||
|
Rd register.CpuRegister
|
||||||
|
Rs1 register.CpuRegister
|
||||||
|
Rs2 register.CpuRegister
|
||||||
|
Imm uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type SubsystemFormatFields struct {
|
||||||
|
Reg register.CpuRegister
|
||||||
|
Sid uint32
|
||||||
|
Sre uint32
|
||||||
|
Cmd uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type DecodedInstr struct {
|
||||||
|
Format InstrFormat
|
||||||
|
OpCode uint32
|
||||||
|
// A, B, C, D1 and D2 formats fields
|
||||||
|
StandardFormatFields
|
||||||
|
// E format fields
|
||||||
|
SubsystemFormatFields
|
||||||
|
}
|
14
pkg/instrset/Instrset.go
Normal file
14
pkg/instrset/Instrset.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package instrset
|
||||||
|
|
||||||
|
import "git.elyanpoujol.fr/elyan/central-arch/pkg/instr"
|
||||||
|
|
||||||
|
var CentralInstructionSet = []instr.InstrDesc{
|
||||||
|
{VariantName: "NOP", Mnemonic: "nop", OpCode: 0x0, Format: instr.D2, Formatter: nil, Behavior: nil},
|
||||||
|
{VariantName: "LDRIR", Mnemonic: "ldr", OpCode: 0x1, Format: instr.A, Formatter: nil, Behavior: nil},
|
||||||
|
{VariantName: "LDRIRW", Mnemonic: "ldr", OpCode: 0x2, Format: instr.A, Formatter: nil, Behavior: nil},
|
||||||
|
{VariantName: "LDRIOW", Mnemonic: "ldr", OpCode: 0x3, Format: instr.A, Formatter: nil, Behavior: nil},
|
||||||
|
{VariantName: "LDRR", Mnemonic: "ldr", OpCode: 0x4, Format: instr.A, Formatter: nil, Behavior: nil},
|
||||||
|
{VariantName: "LDRRW", Mnemonic: "ldr", OpCode: 0x5, Format: instr.A, Formatter: nil, Behavior: nil},
|
||||||
|
{VariantName: "LDROW", Mnemonic: "ldr", OpCode: 0x6, Format: instr.A, Formatter: nil, Behavior: nil},
|
||||||
|
{VariantName: "SVC", Mnemonic: "svc", OpCode: 0x7f, Format: instr.D2, Formatter: nil, Behavior: nil},
|
||||||
|
}
|
7
pkg/memory/memory.go
Normal file
7
pkg/memory/memory.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package memory
|
||||||
|
|
||||||
|
type Memory interface {
|
||||||
|
Read(addr uint32) (uint32, error)
|
||||||
|
Write(addr uint32, value uint32) error
|
||||||
|
Fetch(addr uint32) (uint32, error)
|
||||||
|
}
|
25
pkg/mmu/mmu.go
Normal file
25
pkg/mmu/mmu.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package mmu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/memory"
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/simcontext"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO Add a field of type MmuState
|
||||||
|
type MMU struct{}
|
||||||
|
|
||||||
|
func (m *MMU) Read(addr uint32) (uint32, error) {
|
||||||
|
panic("Not implemented yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MMU) Write(addr uint32, value uint32) error {
|
||||||
|
panic("Not implemented yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MMU) Fetch(addr uint32) (uint32, error) {
|
||||||
|
panic("Not implemented yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(context simcontext.SimContext, realMemory memory.Memory) *MMU {
|
||||||
|
panic("Not implemented yet")
|
||||||
|
}
|
16
pkg/modes/modes.go
Normal file
16
pkg/modes/modes.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package modes
|
||||||
|
|
||||||
|
type CpuMode uint
|
||||||
|
|
||||||
|
const (
|
||||||
|
USER CpuMode = iota
|
||||||
|
SUPERVISOR
|
||||||
|
FAULT
|
||||||
|
)
|
||||||
|
|
||||||
|
type CpuPrivilegeLevel uint
|
||||||
|
|
||||||
|
const (
|
||||||
|
UNPRIVILEGED CpuPrivilegeLevel = iota
|
||||||
|
PRIVILEGED
|
||||||
|
)
|
52
pkg/ram/ram.go
Normal file
52
pkg/ram/ram.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package ram
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/events"
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/simcontext"
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/simstate"
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/simstep"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RAM struct {
|
||||||
|
state simstate.RamState
|
||||||
|
stepper simstep.SimStepper
|
||||||
|
eventLogger events.EventLogger
|
||||||
|
}
|
||||||
|
|
||||||
|
const alignment = 4
|
||||||
|
|
||||||
|
func New(context simcontext.SimContext, bytes uint) (*RAM, error) {
|
||||||
|
if bytes < alignment {
|
||||||
|
return nil, errors.New("memory size should be at least 4 bytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &RAM{
|
||||||
|
state: simstate.RamState{Data: make([]uint32, bytes/alignment)},
|
||||||
|
stepper: context.Stepper,
|
||||||
|
eventLogger: context.EventLogger,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RAM) Read(addr uint32) (uint32, error) {
|
||||||
|
if addr%alignment != 0 {
|
||||||
|
return 0, fmt.Errorf("unaligned memory address: %08x", addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.state.Data[addr/alignment], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RAM) Write(addr uint32, value uint32) error {
|
||||||
|
if addr%alignment != 0 {
|
||||||
|
return fmt.Errorf("unaligned memory address: %08x", addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.state.Data[addr/alignment] = value
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *RAM) Fetch(addr uint32) (uint32, error) {
|
||||||
|
return m.Read(addr)
|
||||||
|
}
|
33
pkg/register/register.go
Normal file
33
pkg/register/register.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package register
|
||||||
|
|
||||||
|
type CpuRegister uint32
|
||||||
|
type CpuStatusRegister uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
R0 CpuRegister = iota
|
||||||
|
R1
|
||||||
|
R2
|
||||||
|
R3
|
||||||
|
R4
|
||||||
|
R5
|
||||||
|
R6
|
||||||
|
R7
|
||||||
|
LR CpuRegister = iota + 15
|
||||||
|
PC
|
||||||
|
SP
|
||||||
|
PC_USER
|
||||||
|
SP_USER
|
||||||
|
PC_SYS
|
||||||
|
SP_SYS
|
||||||
|
PC_FAULT
|
||||||
|
SP_FAULT
|
||||||
|
)
|
||||||
|
|
||||||
|
// General purpose registers count
|
||||||
|
const GPR_COUNT = 8
|
||||||
|
|
||||||
|
const (
|
||||||
|
PSR CpuStatusRegister = iota
|
||||||
|
ESR_SVC
|
||||||
|
ESR_FAULT
|
||||||
|
)
|
30
pkg/sim/breakpoint.go
Normal file
30
pkg/sim/breakpoint.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package sim
|
||||||
|
|
||||||
|
type CpuBreakpoint struct {
|
||||||
|
Addr uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type WatchpointType uint
|
||||||
|
|
||||||
|
const (
|
||||||
|
READ WatchpointType = iota
|
||||||
|
WRITE
|
||||||
|
READ_WRITE
|
||||||
|
)
|
||||||
|
|
||||||
|
type CpuWatchpoint struct {
|
||||||
|
Addr uint32
|
||||||
|
Type WatchpointType
|
||||||
|
}
|
||||||
|
|
||||||
|
type CpuModeTransitionBreakType uint
|
||||||
|
|
||||||
|
const (
|
||||||
|
NONE CpuModeTransitionBreakType = iota
|
||||||
|
ALL
|
||||||
|
USER
|
||||||
|
SUPERVISOR
|
||||||
|
FAULT
|
||||||
|
PRIVILEGED
|
||||||
|
UNPRIVILEGED
|
||||||
|
)
|
3
pkg/sim/commands.go
Normal file
3
pkg/sim/commands.go
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
package sim
|
||||||
|
|
||||||
|
type Command interface{}
|
102
pkg/sim/scheduler.go
Normal file
102
pkg/sim/scheduler.go
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
package sim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/breakcfg"
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/events"
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/simstep"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SchedulerConfig struct {
|
||||||
|
BreakableEventConfig *breakcfg.BreakableEventConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
type Scheduler struct {
|
||||||
|
config SchedulerConfig
|
||||||
|
cpuBreakpoints map[CpuBreakpoint]bool
|
||||||
|
cpuWatchpoints map[CpuWatchpoint]bool
|
||||||
|
cpuModeTransitionBreakType CpuModeTransitionBreakType
|
||||||
|
stepRequest chan events.SimEvent
|
||||||
|
stepResponse chan bool
|
||||||
|
commandRequest chan Command
|
||||||
|
commandReponse chan bool
|
||||||
|
breakEvent chan *Sim
|
||||||
|
quitEvent <-chan bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewScheduler(config SchedulerConfig, quitEvent <-chan bool) *Scheduler {
|
||||||
|
return &Scheduler{
|
||||||
|
config: config,
|
||||||
|
cpuBreakpoints: make(map[CpuBreakpoint]bool),
|
||||||
|
cpuWatchpoints: make(map[CpuWatchpoint]bool),
|
||||||
|
// When the scheduler stopped running, we don't want the client to block
|
||||||
|
// when sending to this channel
|
||||||
|
stepRequest: make(chan events.SimEvent, 1),
|
||||||
|
stepResponse: make(chan bool),
|
||||||
|
// When the scheduler stopped running, we don't want the client to block
|
||||||
|
// when sending to this channel
|
||||||
|
commandRequest: make(chan Command, 1),
|
||||||
|
commandReponse: make(chan bool),
|
||||||
|
breakEvent: make(chan *Sim),
|
||||||
|
quitEvent: quitEvent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scheduler) Run(wg *sync.WaitGroup) {
|
||||||
|
wg.Add(1)
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-s.quitEvent:
|
||||||
|
s.closeSendingChannels()
|
||||||
|
return
|
||||||
|
|
||||||
|
case req := <-s.stepRequest:
|
||||||
|
s.handleStepRequest(req)
|
||||||
|
|
||||||
|
case cmd := <-s.commandRequest:
|
||||||
|
s.handleSchedulingCommand(cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scheduler) closeSendingChannels() {
|
||||||
|
close(s.stepResponse)
|
||||||
|
close(s.commandReponse)
|
||||||
|
close(s.breakEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scheduler) Cleanup() {
|
||||||
|
close(s.stepRequest)
|
||||||
|
close(s.commandRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scheduler) handleStepRequest(req simstep.SimStepRequest) {
|
||||||
|
// TODO Handle request
|
||||||
|
// TODO Check breakpoints
|
||||||
|
log.Println("step !")
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
s.stepResponse <- true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scheduler) handleSchedulingCommand(cmd Command) {
|
||||||
|
// TODO Handle scheduling command
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scheduler) RequestStep(req simstep.SimStepRequest) error {
|
||||||
|
s.stepRequest <- req
|
||||||
|
|
||||||
|
// The scheduler always sends true, to receive false means that the channel is closed
|
||||||
|
// and thus, that we must quit
|
||||||
|
doContinue := <-s.stepResponse
|
||||||
|
|
||||||
|
if !doContinue {
|
||||||
|
return simstep.SimQuitRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
156
pkg/sim/sim.go
Normal file
156
pkg/sim/sim.go
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
package sim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/cache"
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/cpu"
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/events"
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/instr"
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/memory"
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/mmu"
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/ram"
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/simcontext"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SimStatus struct {
|
||||||
|
sync.Mutex
|
||||||
|
Started bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type Sim struct {
|
||||||
|
status SimStatus
|
||||||
|
config SimConfig
|
||||||
|
cpu *cpu.Cpu
|
||||||
|
ram *ram.RAM
|
||||||
|
cache *cache.Cache
|
||||||
|
mmu *mmu.MMU
|
||||||
|
scheduler *Scheduler
|
||||||
|
simQuitEvent chan bool
|
||||||
|
schedulerQuitEvent chan<- bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type SimConfig struct {
|
||||||
|
RamSize uint
|
||||||
|
UseMMU bool
|
||||||
|
UseCache bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(simConfig SimConfig, schedulerConfig SchedulerConfig, eventLogger events.EventLogger) *Sim {
|
||||||
|
var cpuMemory memory.Memory
|
||||||
|
var mmuMemory *mmu.MMU
|
||||||
|
var cacheMemory *cache.Cache
|
||||||
|
|
||||||
|
// We make the channel buffered to not cause wait for the scheduler to read the quit event
|
||||||
|
// as we also wait on the wait group
|
||||||
|
schedulerQuitEvent := make(chan bool, 1)
|
||||||
|
simScheduler := NewScheduler(schedulerConfig, schedulerQuitEvent)
|
||||||
|
|
||||||
|
context := simcontext.SimContext{EventLogger: eventLogger, Stepper: simScheduler}
|
||||||
|
|
||||||
|
ram, err := ram.New(context, simConfig.RamSize)
|
||||||
|
// By default, the CPU memory is the RAM
|
||||||
|
cpuMemory = ram
|
||||||
|
|
||||||
|
if simConfig.UseMMU && !simConfig.UseCache {
|
||||||
|
// Wrap the RAM behind MMU
|
||||||
|
mmuMemory = mmu.New(context, ram)
|
||||||
|
cpuMemory = mmuMemory
|
||||||
|
} else if !simConfig.UseMMU && simConfig.UseCache {
|
||||||
|
// Wrap the RAM behind Cache
|
||||||
|
cacheMemory = cache.New(context, ram)
|
||||||
|
cpuMemory = cacheMemory
|
||||||
|
} else if simConfig.UseMMU && simConfig.UseCache {
|
||||||
|
// Wrap the RAM behind Cache and wrap Cache behind MMU
|
||||||
|
cacheMemory = cache.New(context, ram)
|
||||||
|
mmuMemory = mmu.New(context, cacheMemory)
|
||||||
|
cpuMemory = mmuMemory
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu := cpu.New(context, cpuMemory)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Sim{
|
||||||
|
config: simConfig,
|
||||||
|
cpu: cpu,
|
||||||
|
ram: ram,
|
||||||
|
cache: cacheMemory,
|
||||||
|
mmu: mmuMemory,
|
||||||
|
scheduler: simScheduler,
|
||||||
|
simQuitEvent: make(chan bool),
|
||||||
|
schedulerQuitEvent: schedulerQuitEvent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Sim) Start() {
|
||||||
|
s.setStarted(true)
|
||||||
|
defer s.setStarted(false)
|
||||||
|
|
||||||
|
wg := &sync.WaitGroup{}
|
||||||
|
|
||||||
|
go s.cpu.Run(wg)
|
||||||
|
go s.scheduler.Run(wg)
|
||||||
|
|
||||||
|
// Wait for quit event
|
||||||
|
<-s.simQuitEvent
|
||||||
|
// Signal the scheduler to quit
|
||||||
|
s.schedulerQuitEvent <- false
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
// Do some cleanup
|
||||||
|
s.scheduler.Cleanup()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Sim) Stop() {
|
||||||
|
if s.started() {
|
||||||
|
s.simQuitEvent <- false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Sim) RegisterInstructionSet(instrSet []instr.InstrDesc) {
|
||||||
|
for _, instr := range instrSet {
|
||||||
|
s.cpu.RegisterInstr(instr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Sim) Cpu() *cpu.Cpu {
|
||||||
|
return s.cpu
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Sim) Ram() *ram.RAM {
|
||||||
|
return s.ram
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Sim) UseMMU() bool {
|
||||||
|
return s.config.UseMMU
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Sim) MMU() *mmu.MMU {
|
||||||
|
return s.mmu
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Sim) UseCache() bool {
|
||||||
|
return s.config.UseCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Sim) Cache() *cache.Cache {
|
||||||
|
return s.cache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Sim) setStarted(started bool) {
|
||||||
|
s.status.Lock()
|
||||||
|
defer s.status.Unlock()
|
||||||
|
|
||||||
|
s.status.Started = started
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Sim) started() bool {
|
||||||
|
s.status.Lock()
|
||||||
|
defer s.status.Unlock()
|
||||||
|
|
||||||
|
return s.status.Started
|
||||||
|
}
|
11
pkg/simcontext/context.go
Normal file
11
pkg/simcontext/context.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package simcontext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/events"
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/simstep"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SimContext struct {
|
||||||
|
EventLogger events.EventLogger
|
||||||
|
Stepper simstep.SimStepper
|
||||||
|
}
|
44
pkg/simstate/state.go
Normal file
44
pkg/simstate/state.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package simstate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/register"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CpuRegisters struct {
|
||||||
|
Gprs [register.GPR_COUNT]uint32
|
||||||
|
Lr uint32
|
||||||
|
PcUser uint32
|
||||||
|
SpUser uint32
|
||||||
|
PcSvc uint32
|
||||||
|
SpSvc uint32
|
||||||
|
PcFault uint32
|
||||||
|
SpFault uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type CpuStatusRegisters struct {
|
||||||
|
Psr uint32
|
||||||
|
EsrSvc uint32
|
||||||
|
EsrFault uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type CpuState struct {
|
||||||
|
Registers CpuRegisters
|
||||||
|
StatusRegisters CpuStatusRegisters
|
||||||
|
}
|
||||||
|
|
||||||
|
type RamState struct {
|
||||||
|
Data []uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Implement it
|
||||||
|
type CacheState struct{}
|
||||||
|
|
||||||
|
// TODO Implement it
|
||||||
|
type MmuState struct{}
|
||||||
|
|
||||||
|
type SimState struct {
|
||||||
|
CpuState *CpuState
|
||||||
|
RamState *RamState
|
||||||
|
CacheState *CacheState
|
||||||
|
MmuState *MmuState
|
||||||
|
}
|
17
pkg/simstep/stepper.go
Normal file
17
pkg/simstep/stepper.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package simstep
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/events"
|
||||||
|
)
|
||||||
|
|
||||||
|
var SimQuitRequest = errors.New("simulation quit requested")
|
||||||
|
|
||||||
|
type SimStepRequest interface {
|
||||||
|
events.SimEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
type SimStepper interface {
|
||||||
|
RequestStep(req SimStepRequest) error
|
||||||
|
}
|
6
pkg/subsystem/subsystem.go
Normal file
6
pkg/subsystem/subsystem.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package subsystem
|
||||||
|
|
||||||
|
type Subsystem interface {
|
||||||
|
Read(register uint8, cmd uint8) (uint32, error)
|
||||||
|
Write(register uint8, cmd uint8, value uint32) error
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user