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 }