From 341dcc48410a786ead4e65d880bb356f92554689 Mon Sep 17 00:00:00 2001 From: whitequark Date: Fri, 21 Dec 2018 01:53:32 +0000 Subject: [PATCH] hdl.mem: implement memories. --- doc/COMPAT_SUMMARY.md | 11 ++++-- nmigen/__init__.py | 1 + nmigen/hdl/ast.py | 6 +-- nmigen/hdl/mem.py | 92 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 104 insertions(+), 6 deletions(-) create mode 100644 nmigen/hdl/mem.py diff --git a/doc/COMPAT_SUMMARY.md b/doc/COMPAT_SUMMARY.md index 86ba8f9..f5746a4 100644 --- a/doc/COMPAT_SUMMARY.md +++ b/doc/COMPAT_SUMMARY.md @@ -54,9 +54,14 @@ Compatibility summary - (−) `Tristate` ? - (+) `TSTriple` → `.lib.io.TSTriple`, `bits_sign=`→`shape=` - (−) `Instance` ? - - (−) `READ_FIRST`/`WRITE_FIRST`/`NO_CHANGE` ? - - (−) `_MemoryPort` ? - - (−) `Memory` ? + - (−) `Memory` id + - (−) `.get_port` **obs** → `.read_port()` + `.write_port()` + - (−) `_MemoryPort` **obs** +
Note: nMigen separates read and write ports. + - (−) `READ_FIRST`/`WRITE_FIRST` **obs** +
Note: `READ_FIRST` corresponds to `mem.read_port(transparent=False)`, and `WRITE_FIRST` to `mem.read_port(transparent=True)`. + - (-) `NO_CHANGE` **brk** +
Note: in designs using `NO_CHANGE`, repalce it with an asynchronous read port and logic implementing required semantics explicitly. - (−) `structure` → `.hdl.ast` - (+) `DUID` id - (+) `_Value` → `Value` diff --git a/nmigen/__init__.py b/nmigen/__init__.py index ce1afeb..a9ed14d 100644 --- a/nmigen/__init__.py +++ b/nmigen/__init__.py @@ -2,6 +2,7 @@ from .hdl.ast import Value, Const, C, Mux, Cat, Repl, Array, Signal, ClockSignal from .hdl.dsl import Module from .hdl.cd import ClockDomain from .hdl.ir import Fragment, Instance +from .hdl.mem import Memory from .hdl.xfrm import ResetInserter, CEInserter from .lib.cdc import MultiReg diff --git a/nmigen/hdl/ast.py b/nmigen/hdl/ast.py index e890b4a..52e7dd3 100644 --- a/nmigen/hdl/ast.py +++ b/nmigen/hdl/ast.py @@ -236,7 +236,7 @@ class Const(Value): shape = shape, self.value < 0 self.nbits, self.signed = shape if not isinstance(self.nbits, int) or self.nbits < 0: - raise TypeError("Width must be a non-negative integer") + raise TypeError("Width must be a non-negative integer, not '{!r}'", self.nbits) self.value = self.normalize(self.value, shape) def shape(self): @@ -1067,7 +1067,7 @@ class ValueSet(_MappedKeySet): class SignalKey: def __init__(self, signal): if type(signal) is not Signal: - raise TypeError("Object '{!r}' is not an nMigen signal") + raise TypeError("Object '{!r}' is not an nMigen signal".format(signal)) self.signal = signal def __hash__(self): @@ -1080,7 +1080,7 @@ class SignalKey: def __lt__(self, other): if type(other) is not SignalKey: - raise TypeError("Object '{!r}' cannot be compared to a SignalKey") + raise TypeError("Object '{!r}' cannot be compared to a SignalKey".format(signal)) return self.signal.duid < other.signal.duid def __repr__(self): diff --git a/nmigen/hdl/mem.py b/nmigen/hdl/mem.py new file mode 100644 index 0000000..3091890 --- /dev/null +++ b/nmigen/hdl/mem.py @@ -0,0 +1,92 @@ +import traceback + +from .. import tracer +from .ast import * +from .ir import Instance + + +class Memory: + def __init__(self, width, depth, init=None, name=None): + if not isinstance(width, int) or width < 0: + raise TypeError("Memory width must be a non-negative integer, not '{!r}'" + .format(width)) + if not isinstance(depth, int) or depth < 0: + raise TypeError("Memory depth must be a non-negative integer, not '{!r}'" + .format(depth)) + + tb = traceback.extract_stack(limit=2) + self.src_loc = (tb[0].filename, tb[0].lineno) + + if name is None: + try: + name = tracer.get_var_name(depth=2) + except tracer.NameNotFound: + name = "$memory" + self.name = name + + self.width = width + self.depth = depth + self.init = None if init is None else list(init) + + def read_port(self, domain="sync", asynchronous=False, transparent=True): + return ReadPort(self, domain, asynchronous, transparent) + + def write_port(self, domain="sync", priority=0, granularity=None): + if granularity is None: + granularity = self.width + if not isinstance(granularity, int) or granularity < 0 or granularity > self.width: + raise TypeError("Write port granularity must be a non-negative integer not greater " + "than memory width, not '{!r}'" + .format(granularity)) + return WritePort(self, domain, priority, granularity) + + +class ReadPort: + def __init__(self, memory, domain, asynchronous, transparent): + self.memory = memory + self.domain = domain + self.asynchronous = asynchronous + self.transparent = transparent + + self.addr = Signal(max=memory.depth) + self.data = Signal(memory.width) + self.en = Signal() + + def get_fragment(self, platform): + return Instance("$memrd", + p_MEMID=self.memory, + p_ABITS=self.addr.nbits, + p_WIDTH=self.data.nbits, + p_CLK_ENABLE=not self.asynchronous, + p_CLK_POLARITY=1, + p_TRANSPARENT=self.transparent, + i_CLK=ClockSignal(self.domain), + i_EN=self.en, + i_ADDR=self.addr, + o_DATA=self.data, + ) + +class WritePort: + def __init__(self, memory, domain, priority, granularity): + self.memory = memory + self.domain = domain + self.priority = priority + self.granularity = granularity + + self.addr = Signal(max=memory.depth) + self.data = Signal(memory.width) + self.en = Signal(memory.width // granularity) + + def get_fragment(self, platform): + return Instance("$memwr", + p_MEMID=self.memory, + p_ABITS=self.addr.nbits, + p_WIDTH=self.data.nbits, + p_CLK_ENABLE=1, + p_CLK_POLARITY=1, + p_PRIORITY=self.priority, + i_CLK=ClockSignal(self.domain), + i_EN=Cat(Repl(en_bit, self.granularity) for en_bit in self.en), + i_ADDR=self.addr, + i_DATA=self.data, + ) -- 2.30.2