central-arch/pkg/sim/sim.go

157 lines
3.3 KiB
Go
Raw Normal View History

2024-11-01 10:54:59 +00:00
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
}