157 lines
3.3 KiB
Go
157 lines
3.3 KiB
Go
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/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"
|
|
"git.elyanpoujol.fr/elyan/central-arch/pkg/siminstr"
|
|
)
|
|
|
|
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 []siminstr.SimInstrDesc) {
|
|
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
|
|
}
|