Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/nmigen
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 6672ab2e3fd7
Choose a base ref
...
head repository: m-labs/nmigen
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 2b4a8510ca36
Choose a head ref
  • 2 commits
  • 7 files changed
  • 1 contributor

Commits on Dec 21, 2018

  1. hdl.mem: implement memories.

    whitequark committed Dec 21, 2018
    Copy the full SHA
    6d9a6b5 View commit details
  2. back.rtlil: implement memories.

    whitequark committed Dec 21, 2018
    Copy the full SHA
    2b4a851 View commit details
Showing with 192 additions and 17 deletions.
  1. +8 −3 doc/COMPAT_SUMMARY.md
  2. +31 −0 examples/mem.py
  3. +1 −0 nmigen/__init__.py
  4. +54 −11 nmigen/back/rtlil.py
  5. +3 −0 nmigen/back/verilog.py
  6. +3 −3 nmigen/hdl/ast.py
  7. +92 −0 nmigen/hdl/mem.py
11 changes: 8 additions & 3 deletions doc/COMPAT_SUMMARY.md
Original file line number Diff line number Diff line change
@@ -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**
<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`
- (+) `DUID` id
- (+) `_Value``Value`
31 changes: 31 additions & 0 deletions examples/mem.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from nmigen import *
from nmigen.back import rtlil, verilog


class RegisterFile:
def __init__(self):
self.adr = Signal(4)
self.dat_r = Signal(8)
self.dat_w = Signal(8)
self.we = Signal()
self.mem = Memory(width=8, depth=16, init=[0xaa, 0x55])

def get_fragment(self, platform):
m = Module()
m.submodules.rdport = rdport = self.mem.read_port()
m.submodules.wrport = wrport = self.mem.write_port()
m.d.comb += [
rdport.addr.eq(self.adr),
self.dat_r.eq(rdport.data),
rdport.en.eq(1),
wrport.addr.eq(self.adr),
wrport.data.eq(self.dat_w),
wrport.en.eq(self.we),
]
return m.lower(platform)


rf = RegisterFile()
frag = rf.get_fragment(platform=None)
# print(rtlil.convert(frag, ports=[rf.adr, rf.dat_r, rf.dat_w, rf.we]))
print(verilog.convert(frag, ports=[rf.adr, rf.dat_r, rf.dat_w, rf.we]))
1 change: 1 addition & 0 deletions nmigen/__init__.py
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@
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
65 changes: 54 additions & 11 deletions nmigen/back/rtlil.py
Original file line number Diff line number Diff line change
@@ -3,7 +3,8 @@
from collections import defaultdict, OrderedDict
from contextlib import contextmanager

from ..hdl import ast, ir, xfrm
from ..tools import bits_for
from ..hdl import ast, ir, mem, xfrm


class _Namer:
@@ -96,16 +97,23 @@ def wire(self, width, port_id=None, port_kind=None, name=None, src=""):
def connect(self, lhs, rhs):
self._append(" connect {} {}\n", lhs, rhs)

def memory(self, width, size, name=None, src=""):
self._src(src)
name = self._make_name(name, local=False)
self._append(" memory width {} size {} {}\n", width, size, name)
return name

def cell(self, kind, name=None, params={}, ports={}, src=""):
self._src(src)
name = self._make_name(name, local=True)
name = self._make_name(name, local=False)
self._append(" cell {} {}\n", kind, name)
for param, value in params.items():
if isinstance(value, str):
value = repr(value)
self._append(" parameter \\{} \"{}\"\n",
param, value.translate(self._escape_map))
else:
value = int(value)
self._append(" parameter \\{} {}\n", param, value)
self._append(" parameter \\{} {:d}\n",
param, value)
for port, wire in ports.items():
self._append(" connect {} {}\n", port, wire)
self._append(" end\n")
@@ -572,7 +580,10 @@ def convert_fragment(builder, fragment, name, top):
for port_name, value in fragment.named_ports.items():
port_map["\\{}".format(port_name)] = value

return "\\{}".format(fragment.type), port_map
if fragment.type[0] == "$":
return fragment.type, port_map
else:
return "\\{}".format(fragment.type), port_map

with builder.module(name or "anonymous", attrs={"top": 1} if top else {}) as module:
compiler_state = _ValueCompilerState(module)
@@ -602,13 +613,45 @@ def convert_fragment(builder, fragment, name, top):
# Transform all subfragments to their respective cells. Transforming signals connected
# to their ports into wires eagerly makes sure they get sensible (prefixed with submodule
# name) names.
memories = OrderedDict()
for subfragment, sub_name in fragment.subfragments:
sub_name, sub_port_map = \
sub_params = OrderedDict()
if hasattr(subfragment, "parameters"):
for param_name, param_value in subfragment.parameters.items():
if isinstance(param_value, mem.Memory):
memory = param_value
if memory not in memories:
memories[memory] = module.memory(width=memory.width, size=memory.depth,
name=memory.name)
addr_bits = bits_for(memory.depth)
for addr, data in enumerate(memory.init):
module.cell("$meminit", ports={
"\\ADDR": rhs_compiler(ast.Const(addr, addr_bits)),
"\\DATA": rhs_compiler(ast.Const(data, memory.width)),
}, params={
"MEMID": memories[memory],
"ABITS": addr_bits,
"WIDTH": memory.width,
"WORDS": 1,
"PRIORITY": 0,
})

param_value = memories[memory]

sub_params[param_name] = param_value

sub_type, sub_port_map = \
convert_fragment(builder, subfragment, top=False, name=sub_name)
module.cell(sub_name, name=sub_name, ports={
port: compiler_state.resolve_curr(signal, prefix=sub_name)
for port, signal in sub_port_map.items()
}, params=subfragment.parameters)

sub_ports = OrderedDict()
for port, value in sub_port_map.items():
if isinstance(value, ast.Signal):
sigspec = compiler_state.resolve_curr(value, prefix=sub_name)
else:
sigspec = rhs_compiler(value)
sub_ports[port] = sigspec

module.cell(sub_type, name=sub_name, ports=sub_ports, params=sub_params)

with module.process() as process:
with process.case() as case:
3 changes: 3 additions & 0 deletions nmigen/back/verilog.py
Original file line number Diff line number Diff line change
@@ -27,8 +27,11 @@ def convert(*args, **kwargs):
proc_arst
proc_dff
proc_clean
design -save orig
memory_collect
write_verilog
# Make sure there are no undriven wires in generated RTLIL.
design -load orig
proc
select -assert-none w:* i:* %a %d o:* %a %ci* %d c:* %co* %a %d n:$* %d
""".format(il_text))
6 changes: 3 additions & 3 deletions nmigen/hdl/ast.py
Original file line number Diff line number Diff line change
@@ -236,7 +236,7 @@ def __init__(self, value, shape=None):
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 @@ def __eq__(self, other):

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):
92 changes: 92 additions & 0 deletions nmigen/hdl/mem.py
Original file line number Diff line number Diff line change
@@ -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,
)