hdl.mem: implement memories.
This commit is contained in:
parent
6672ab2e3f
commit
6d9a6b5d84
|
@ -54,9 +54,14 @@ Compatibility summary
|
||||||
- (−) `Tristate` ?
|
- (−) `Tristate` ?
|
||||||
- (+) `TSTriple` → `.lib.io.TSTriple`, `bits_sign=`→`shape=`
|
- (+) `TSTriple` → `.lib.io.TSTriple`, `bits_sign=`→`shape=`
|
||||||
- (−) `Instance` ?
|
- (−) `Instance` ?
|
||||||
- (−) `READ_FIRST`/`WRITE_FIRST`/`NO_CHANGE` ?
|
- (−) `Memory` id
|
||||||
- (−) `_MemoryPort` ?
|
- (−) `.get_port` **obs** → `.read_port()` + `.write_port()`
|
||||||
- (−) `Memory` ?
|
- (−) `_MemoryPort` **obs**
|
||||||
|
<br>Note: nMigen separates read and write ports.
|
||||||
|
- (−) `READ_FIRST`/`WRITE_FIRST` **obs**
|
||||||
|
<br>Note: `READ_FIRST` corresponds to `mem.read_port(transparent=False)`, and `WRITE_FIRST` to `mem.read_port(transparent=True)`.
|
||||||
|
- (-) `NO_CHANGE` **brk**
|
||||||
|
<br>Note: in designs using `NO_CHANGE`, repalce it with an asynchronous read port and logic implementing required semantics explicitly.
|
||||||
- (−) `structure` → `.hdl.ast`
|
- (−) `structure` → `.hdl.ast`
|
||||||
- (+) `DUID` id
|
- (+) `DUID` id
|
||||||
- (+) `_Value` → `Value`
|
- (+) `_Value` → `Value`
|
||||||
|
|
|
@ -2,6 +2,7 @@ from .hdl.ast import Value, Const, C, Mux, Cat, Repl, Array, Signal, ClockSignal
|
||||||
from .hdl.dsl import Module
|
from .hdl.dsl import Module
|
||||||
from .hdl.cd import ClockDomain
|
from .hdl.cd import ClockDomain
|
||||||
from .hdl.ir import Fragment, Instance
|
from .hdl.ir import Fragment, Instance
|
||||||
|
from .hdl.mem import Memory
|
||||||
from .hdl.xfrm import ResetInserter, CEInserter
|
from .hdl.xfrm import ResetInserter, CEInserter
|
||||||
|
|
||||||
from .lib.cdc import MultiReg
|
from .lib.cdc import MultiReg
|
||||||
|
|
|
@ -236,7 +236,7 @@ class Const(Value):
|
||||||
shape = shape, self.value < 0
|
shape = shape, self.value < 0
|
||||||
self.nbits, self.signed = shape
|
self.nbits, self.signed = shape
|
||||||
if not isinstance(self.nbits, int) or self.nbits < 0:
|
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)
|
self.value = self.normalize(self.value, shape)
|
||||||
|
|
||||||
def shape(self):
|
def shape(self):
|
||||||
|
@ -1067,7 +1067,7 @@ class ValueSet(_MappedKeySet):
|
||||||
class SignalKey:
|
class SignalKey:
|
||||||
def __init__(self, signal):
|
def __init__(self, signal):
|
||||||
if type(signal) is not 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
|
self.signal = signal
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
|
@ -1080,7 +1080,7 @@ class SignalKey:
|
||||||
|
|
||||||
def __lt__(self, other):
|
def __lt__(self, other):
|
||||||
if type(other) is not SignalKey:
|
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
|
return self.signal.duid < other.signal.duid
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
|
92
nmigen/hdl/mem.py
Normal file
92
nmigen/hdl/mem.py
Normal file
|
@ -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,
|
||||||
|
)
|
Loading…
Reference in a new issue