def __init__(self,cycle=0,fp=None,int=None,mem=None,use_trace=False, no_fma=False): self.no_fma = no_fma self.cycle = cycle self.counter = defaultdict(lambda:0) self.fp = fp if fp is not None else RegisterFile(FPRegister,Core.fpregisters) self.int = int if int is not None else RegisterFile(IntRegister,Core.intregisters) self.mem = mem if mem is not None else [0.0]*Core.memsize self.hazards = Pipeline('Register') self.units = Pipeline('Logic Unit') self.inuse_src = Pipeline('Registers unavailable as source (non-hazard)') self.inuse_dst = Pipeline('Registers unavailable as destination (non-hazard)') self.writethrough = WriteThrough() self.regnames = dict() self.fppool = set(self.fp.keys()) self.fpeternal = set() self.trace = self.trace_print if use_trace else self.trace_none self.use_trace = use_trace # storing this is a dirty hack, only used externally self.cv = CViewer(self)
class Core: memsize = 32 # Number of doubles fpregisters = 32 intregisters = 32 inline_asm = '' def __init__(self,cycle=0,fp=None,int=None,mem=None,use_trace=False, no_fma=False): self.no_fma = no_fma self.cycle = cycle self.counter = defaultdict(lambda:0) self.fp = fp if fp is not None else RegisterFile(FPRegister,Core.fpregisters) self.int = int if int is not None else RegisterFile(IntRegister,Core.intregisters) self.mem = mem if mem is not None else [0.0]*Core.memsize self.hazards = Pipeline('Register') self.units = Pipeline('Logic Unit') self.inuse_src = Pipeline('Registers unavailable as source (non-hazard)') self.inuse_dst = Pipeline('Registers unavailable as destination (non-hazard)') self.writethrough = WriteThrough() self.regnames = dict() self.fppool = set(self.fp.keys()) self.fpeternal = set() self.trace = self.trace_print if use_trace else self.trace_none self.use_trace = use_trace # storing this is a dirty hack, only used externally self.cv = CViewer(self) def __str__(self): return ('Core(cycle=%r,\n\tfp=%s,\n\tint=%s,\n\tmem=%r,\n\tregnames=%s,\n\tcounter=%s)' % (self.cycle,self.fp,self.int,self.mem,self.regnames,dict(self.counter))) def __repr__(self): return ('Core(cycle=%r,\n\tfp=%r,\n\tint=%r,\n\tmem=%r,\n\tregnames=%r)' % (self.cycle,self.fp,self.int,self.mem,self.regnames)) def flush_pipeline(self): self.hazards.flush() self.inuse_src.flush() self.inuse_dst.flush() self.units.flush() self.writethrough.flush() def name_registers(self,**args): self.regnames.update(args) self.fppool.difference_update(args.values()) def gc(self): raise Exception('Garbage collector not implemented') def get_fpregister(self,reg,allocate=True): if isinstance(reg,Register): # The register has been named explicitly self.fppool.discard(reg) return reg elif isinstance(reg,str): # It is a string, find a concrete register phys = self.regnames.get(reg) if phys is None: if not allocate: raise Exception('Register "%s" has not been allocated' % (reg,)) if len(self.fppool) < 1: self.gc() try: phys = self.fppool.pop() except KeyError: raise Exception('Cannot find a free register') self.regnames[reg] = phys return phys else: raise Exception('Invalid register: %r' % reg) def access_fpregisters(self,*args): return (self.fp[self.get_fpregister(reg)] for reg in args) def acquire_fpregisters(self,numbers): regs = list(map(FPRegister,numbers)) # Have to make this a list because otherwise set.update(regs) modifies regs self.fpeternal.update(regs) self.fppool.difference_update(regs) return regs def next_cycle(self): self.cycle += 1 self.hazards.retire() self.inuse_src.retire() self.inuse_dst.retire() self.units.retire() self.writethrough.retire() def trace_none(self,msg): pass def trace_print(self,msg): if isinstance(msg,isa.Instruction): print('[%2d] %s' % (self.cycle,msg)) else: print('[%2d] -- %s' % (self.cycle,msg)) def print_inline(self,instr): self.inline_asm += '%s\n' % self.cv.named_view(instr) def execute_one(self,instr): while self.units.stall((instr.unit,)) > 0: self.trace('Instruction unit in use: %s' % (instr.unit,)) self.next_cycle() while self.hazards.stall(map(self.get_fpregister,instr.read)) > 0: def format_hazards(odict): return ', '.join('(%s:%s,%d)' % (reg,self.get_fpregister(reg,allocate=False),cost) for (reg,cost) in odict.items()) self.trace('Register hazards: %s' % format_hazards(self.hazards.conflicts(instr.read))) self.next_cycle() while self.inuse_src.stall(map(self.get_fpregister,instr.read)) > 0: def format_inuse_src(odict): return ', '.join('(%s:%s,%d)' % (reg,self.get_fpregister(reg,allocate=False),cost) for (reg,cost) in odict.items()) self.trace('Register inuse_src: %s' % format_inuse_src(self.inuse_src.conflicts(instr.read))) self.next_cycle() while self.inuse_dst.stall(map(self.get_fpregister,instr.write)) > 0: def format_inuse_dst(odict): return ', '.join('(%s:%s,%d)' % (reg,self.get_fpregister(reg,allocate=False),cost) for (reg,cost) in odict.items()) self.trace('Register inuse_dst: %s' % format_inuse_dst(self.inuse_dst.conflicts(instr.read))) self.next_cycle() while self.writethrough.stall(instr.writethrough): self.trace('WriteThrough tokens in use') self.next_cycle() self.trace(instr) instr.run(self) self.print_inline(instr) self.counter[instr.unit] += 1 self.units[instr.unit] = instr.ithroughput for reg in instr.write: self.hazards[reg] = instr.latency for reg,(src_latency,dst_latency) in instr.inuse_regs.items(): self.inuse_src[reg] = src_latency self.inuse_dst[reg] = dst_latency self.writethrough.issue(instr.writethrough) def execute(self,code): cycle_start = self.cycle for instr in code: self.execute_one(instr) return self.cycle - cycle_start def cost(self,instr): cost = max(self.units.stall((instr.unit,)), self.hazards.stall((self.get_fpregister(reg,allocate=False) for reg in instr.read)), self.inuse_src.stall((self.get_fpregister(reg,allocate=False) for reg in instr.read)), self.inuse_dst.stall((self.get_fpregister(reg,allocate=False) for reg in instr.write)), self.writethrough.stall(instr.writethrough)) return cost def schedule_one(self,istream): def get_candidates(stream): 'generator for safe instructions' stream_write = set() # Preserves order for read-after-write stream_read = set() # Preserves order for write-after-read for i,instr in enumerate(stream): instr_read = instr.read.union(instr.iread) instr_write = instr.write.union(instr.iwrite) if stream_write.isdisjoint(instr_read) and stream_read.isdisjoint(instr_write): yield i,instr stream_write.update(instr_write) stream_read.update(instr_read) candidates = list(get_candidates(istream)) if len(candidates) < 1: raise Exception('Cannot find a safe instruction') (i,instr) = min(candidates, key=lambda c:self.cost(c[1])) self.execute_one(instr) del istream[i] def schedule(self,istream): cycle_start = self.cycle while len(istream) > 0: self.schedule_one(istream) return self.cycle - cycle_start