-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
504 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
from nnsim.module import Module, HWError | ||
from nnsim.reg import Reg | ||
|
||
class ChannelError(HWError): | ||
pass | ||
|
||
class Channel(Module): | ||
def instantiate(self, depth=2): | ||
self.data = [None]*depth | ||
self.depth = depth | ||
|
||
self.rd_ptr = Reg(0) | ||
self.wr_ptr = Reg(0) | ||
|
||
def peek(self, idx=0): | ||
if not self.valid(idx): | ||
raise ChannelError("Reading from empty channel") | ||
return self.data[(self.rd_ptr.rd() + idx) % self.depth] | ||
|
||
def push(self, x): | ||
if not self.vacancy(): | ||
raise ChannelError("Enqueueing into full channel") | ||
self.data[self.wr_ptr.rd() % self.depth] = x | ||
self.wr_ptr.wr((self.wr_ptr.rd() + 1) % (2*self.depth)) | ||
|
||
def free(self, count=1): | ||
if not self.valid(count-1): | ||
raise ChannelError("Dequeueing from empty channel") | ||
self.rd_ptr.wr((self.rd_ptr.rd() + count) % (2*self.depth)) | ||
|
||
def pop(self): | ||
self.free(1) | ||
return self.peek(0) | ||
|
||
def valid(self, idx=0): | ||
return ((self.wr_ptr.rd() - self.rd_ptr.rd()) % (2*self.depth)) > idx | ||
|
||
def vacancy(self, idx=0): | ||
return ((self.rd_ptr.rd() + self.depth - self.wr_ptr.rd()) % | ||
(2*self.depth)) > idx | ||
|
||
def clear(self): | ||
# Use with care since it conflicts with enq and deq | ||
self.rd_ptr.wr(self.wr_ptr.rd()) | ||
|
||
def EmptyChannel(Channel): | ||
def valid(self, idx=0): | ||
return False | ||
|
||
def FullChannel(Channel): | ||
def vacancy(self): | ||
return False | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
from nnsim.module import HWError | ||
from collections import defaultdict | ||
|
||
class CostError(HWError): | ||
pass | ||
|
||
# default values for 65nm process | ||
# ALU:1, RF:1, PE/LN:2, GB:6, DRAM:200 | ||
|
||
# Track number of accesses, and accumulate total energy (normalized over ALU) | ||
class CostModel: | ||
def init(self,bitwidth=32,registers=128,num_pe=200,buffer_kb=100): | ||
self.uses=defaultdict(int) | ||
|
||
self.cost=defaultdict(int) | ||
self.ALU(bitwidth) | ||
self.RegisterFile(registers, bitwidth) | ||
self.LocalNetwork(num_pe) | ||
self.GlobalBuffer(buffer_kb*1024) | ||
self.DRAM(num_pe) | ||
|
||
def count(self, event, count=1): | ||
self.uses[event] += count | ||
|
||
def ALU(self, bitwidth=32): | ||
self.cost["ALU"] = 1 | ||
|
||
|
||
def RegisterFile(self, registers=128, bitwidth=32): | ||
"""RF register file energy | ||
Dependent on the number of registers. | ||
If within 0.5 - 1kB total assume 1x ALU""" | ||
|
||
bytes = registers * bitwidth / 8 | ||
if bytes > 1024: | ||
raise CostError("Estimate RF cost if you need a larger register file") | ||
self.cost["RF"] = 1 # RF -> PE | ||
# register read/write | ||
|
||
# PE - Local PE network | ||
# assume talking to neighbors only | ||
# for 200-1000 PEs total | ||
def LocalNetwork(self, num_pes = 200): | ||
if num_pes > 1000: | ||
raise CostError("Estimate cost if you need a large number of PEs") | ||
self.cost["LN"] = 2 # PE -> PE | ||
|
||
# GB Global Buffer | ||
# assume all PEs have direct access | ||
# no dynamic network | ||
# for 100-500kB assume 6x | ||
def GlobalBuffer(self, bytes = 100*1024): | ||
if bytes > 500*1024: | ||
raise CostError("Estimate cost if you need a larger Global Buffer") | ||
self.cost["GB"] = 6 # GB -> PE | ||
|
||
def DRAM(self, controllers=1): | ||
self.cost["DRAM"] = 200 # DRAM -> GB | ||
|
||
def energy(self, verbose=True): | ||
"""Tally up energy consumption""" | ||
total=0 | ||
for c in self.uses: | ||
energy = self.cost[c] * self.uses[c] | ||
if verbose: | ||
print("%s\t%d\t%d\t%d\n" % (c, self.cost[c], self.uses[c], energy)) | ||
total += energy | ||
if verbose: | ||
print("%s\t \t\t%d\n" % ("Total", total)) | ||
return total |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
from nnsim.module import Module, HWError | ||
from nnsim.reg import Reg | ||
|
||
class FIFOError(HWError): | ||
pass | ||
|
||
class FIFO(Module): | ||
def instantiate(self, depth=2): | ||
self.data = [0]*depth | ||
self.depth = depth | ||
|
||
self.rd_ptr = Reg(0) | ||
self.wr_ptr = Reg(0) | ||
|
||
def peek(self): | ||
if self.wr_ptr.rd() == self.rd_ptr.rd(): | ||
raise FIFOError("Reading from empty FIFO") | ||
return self.data[self.rd_ptr.rd() % self.depth] | ||
|
||
def enq(self, x): | ||
if (self.wr_ptr.rd() - self.rd_ptr.rd()) % (2*self.depth) == self.depth: | ||
raise FIFOError("Enqueueing into full FIFO") | ||
self.data[self.wr_ptr.rd() % self.depth] = x | ||
self.wr_ptr.wr((self.wr_ptr.rd() + 1) % (2*self.depth)) | ||
|
||
def deq(self): | ||
if self.wr_ptr.rd() == self.rd_ptr.rd(): | ||
raise FIFOError("Dequeueing from empty FIFO") | ||
self.rd_ptr.wr((self.rd_ptr.rd() + 1) % (2*self.depth)) | ||
|
||
def not_full(self): | ||
return not ((self.wr_ptr.rd() - self.rd_ptr.rd()) % (2*self.depth) == self.depth) | ||
|
||
def not_empty(self): | ||
return not (self.wr_ptr.rd() == self.rd_ptr.rd()) | ||
|
||
def reset(self): | ||
self.rd_ptr.wr(self.wr_ptr.rd()) | ||
|
||
class WindowFIFO(Module): | ||
def instantiate(self, depth, peek_window, enq_window, deq_window): | ||
self.data = [0]*depth | ||
self.depth = depth | ||
self.peek_window = peek_window | ||
self.enq_window = enq_window | ||
self.deq_window = deq_window | ||
|
||
self.rd_ptr = Reg(0) | ||
self.wr_ptr = Reg(0) | ||
|
||
def peek(self): | ||
if (self.wr_ptr.rd() - self.rd_ptr.rd()) % (2*self.depth) \ | ||
< self.peek_window: | ||
raise FIFOError("Reading from empty FIFO") | ||
peek_output = [0]*self.peek_window | ||
for i in xrange(self.peek_window): | ||
peek_output[i] = self.data[(self.rd_ptr.rd() + i)% self.depth] | ||
return peek_output | ||
|
||
def enq(self, x): | ||
if (self.wr_ptr.rd() - self.rd_ptr.rd() + self.enq_window - 1) % \ | ||
(2*self.depth) >= self.depth: | ||
raise FIFOError("Enqueueing into full FIFO") | ||
for i in xrange(self.enq_window): | ||
self.data[(self.wr_ptr.rd() + i) % self.depth] = x[i] | ||
self.wr_ptr.wr((self.wr_ptr.rd() + self.enq_window) % (2*self.depth)) | ||
|
||
def deq(self): | ||
if (self.wr_ptr.rd() - self.rd_ptr.rd()) % (2*self.depth) \ | ||
< self.deq_window: | ||
raise FIFOError("Dequeueing from empty FIFO") | ||
self.rd_ptr.wr((self.rd_ptr.rd() + self.deq_window) % (2*self.depth)) | ||
|
||
def not_full(self): | ||
return not ((self.wr_ptr.rd() - self.rd_ptr.rd() + self.enq_window - 1) % | ||
(2*self.depth) >= self.depth) | ||
|
||
def valid(self): | ||
return not ((self.wr_ptr.rd() - self.rd_ptr.rd()) % (2*self.depth) | ||
< self.peek_window) | ||
|
||
def not_empty(self): | ||
return not ((self.wr_ptr.rd() - self.rd_ptr.rd()) % (2*self.depth) < self.deq_window) | ||
|
||
def clear(self): | ||
self.rd_ptr.wr(self.wr_ptr.rd()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
# Module object | ||
|
||
class HWError(Exception): | ||
pass | ||
|
||
class Module(object): | ||
def __init__(self, *args, **kwargs): | ||
self.name = str(id(self)) | ||
self.path = "" | ||
self.sub_modules = [] | ||
|
||
self.stat_type = 'hide' | ||
self.raw_stats = {} | ||
self.final_stats = {} | ||
|
||
self.instantiate(*args, **kwargs) | ||
self.register_modules() | ||
|
||
def setup(self): | ||
pass | ||
|
||
def __setup__(self, path=''): | ||
self.setup() | ||
self.path = "%s/%s" % (path, self.name) | ||
for sub_module in self.sub_modules: | ||
sub_module.__setup__(self.path) | ||
|
||
def instantiate(self): | ||
raise HWError("Cannot instantiate abstract module") | ||
|
||
def finalize_stats(self): | ||
self.final_stats = { k:v for k, v in self.raw_stats.items() } | ||
for sub_module in self.sub_modules: | ||
sub_module.finalize_stats() | ||
if sub_module.stat_type == 'aggregate': | ||
for key in sub_module.final_stats: | ||
if key in self.final_stats: | ||
self.final_stats[key] += sub_module.final_stats[key] | ||
else: | ||
self.final_stats[key] = sub_module.final_stats[key] | ||
|
||
def dump_stats(self): | ||
if self.stat_type == 'show': | ||
print("%s: " % self.path) | ||
for key in self.final_stats: | ||
print("\t%s: %s" % (key, self.final_stats[key])) | ||
for sub_module in self.sub_modules: | ||
sub_module.dump_stats() | ||
|
||
def register_modules(self): | ||
for attr in vars(self).values(): | ||
if issubclass(type(attr), Module): | ||
self.sub_modules.append(attr) | ||
elif issubclass(type(attr), ModuleList): | ||
self.sub_modules += attr.register() | ||
|
||
def tick(self): | ||
pass | ||
|
||
def reset(self): | ||
pass | ||
|
||
def __tick__(self): | ||
self.tick() | ||
for sub_module in self.sub_modules: | ||
sub_module.__tick__() | ||
|
||
def __ntick__(self): | ||
for sub_module in self.sub_modules: | ||
sub_module.__ntick__() | ||
|
||
def __reset__(self): | ||
self.reset() | ||
for sub_module in self.sub_modules: | ||
sub_module.__reset__() | ||
|
||
class ModuleList(object): | ||
def __init__(self): | ||
self.list = [] | ||
|
||
def append(self, m): | ||
if issubclass(type(m), Module) or issubclass(type(m), ModuleList): | ||
self.list.append(m) | ||
else: | ||
raise HWError("Can only append Module or ModuleList") | ||
|
||
def __getitem__(self, key): | ||
return self.list[key] | ||
|
||
|
||
def register(self): | ||
module_list = [] | ||
for m in self.list: | ||
if issubclass(type(m), Module): | ||
module_list.append(m) | ||
elif issubclass(type(m), ModuleList): | ||
module_list += m.register() | ||
return module_list | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
from nnsim.fifo import FIFO | ||
|
||
class Pipeline(Module): | ||
def instantiate(self, stages=2): | ||
self.stages = [] | ||
for stage in xrange(stages): | ||
self.stages.append(FIFO(depth=1)) | ||
|
||
def rd(self): | ||
if not self.rd_rdy(): | ||
raise FIFOError("Pipeline not ready for read") | ||
self.stages[-1].deq() | ||
return self.stages[-1].peek() | ||
|
||
def wr(self, x): | ||
if not self.wr_rdy(): | ||
raise FIFOError("Pipeline not ready for write") | ||
self.stages[0].enq(x) | ||
|
||
def deq(self): | ||
if self.wr_ptr.rd() == self.rd_ptr.rd(): | ||
raise FIFOError("Dequeueing from empty FIFO") | ||
self.rd_ptr.wr((self.rd_ptr.rd() + 1) % (2*self.depth)) | ||
|
||
def wr_rdy(self): | ||
return self.stages[0].not_full() | ||
|
||
def rd_rdy(self): | ||
return self.stages[-1].not_empty() |
Oops, something went wrong.