From e16353a281bdfb65547a20575f2a5d96bd744f8e Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Mon, 10 Sep 2012 23:45:02 +0200 Subject: [PATCH 1/3] Multi-clock design support + new instance API --- migen/fhdl/structure.py | 100 ++++++++++++++++++++++----- migen/fhdl/tools.py | 37 ++++++---- migen/fhdl/verilog.py | 72 ++++++++++--------- migen/fhdl/verilog_mem_behavioral.py | 6 +- migen/sim/generic.py | 24 ++++--- 5 files changed, 164 insertions(+), 75 deletions(-) diff --git a/migen/fhdl/structure.py b/migen/fhdl/structure.py index 546066e26..307bf6572 100644 --- a/migen/fhdl/structure.py +++ b/migen/fhdl/structure.py @@ -1,6 +1,7 @@ import math import inspect import re +from collections import defaultdict from migen.fhdl import tracer @@ -240,26 +241,49 @@ class Array(list): # extras class Instance: - def __init__(self, of, outs=[], ins=[], inouts=[], parameters=[], clkport="", rstport="", name=""): + def __init__(self, of, *items, name=""): self.of = of if name: self.name_override = name else: self.name_override = of - def process_io(x): - if isinstance(x[1], Signal): - return x # override - elif isinstance(x[1], BV): - return (x[0], Signal(x[1], x[0])) + self.items = items + + class _IO: + def __init__(self, name, signal_or_bv): + self.name = name + if isinstance(signal_or_bv, Signal): + self.signal = signal_or_bv + elif isinstance(signal_or_bv, BV): + self.signal = Signal(signal_or_bv, name) else: raise TypeError - self.outs = dict(map(process_io, outs)) - self.ins = dict(map(process_io, ins)) - self.inouts = dict(map(process_io, inouts)) - self.parameters = parameters - self.clkport = clkport - self.rstport = rstport + class Input(_IO): + pass + class Output(_IO): + pass + class InOut(_IO): + pass + class Parameter: + def __init__(self, name, value): + self.name = name + self.value = value + + class _CR: + def __init__(self, name_inst, domain="sys"): + self.name_inst = name_inst + self.domain = domain + class ClockPort(_CR): + pass + class ResetPort(_CR): + pass + + def get_io(self, name): + for item in self.items: + if isinstance(item, Instance._IO) and item.name == name: + return item.signal + def __hash__(self): return id(self) @@ -267,7 +291,8 @@ class Instance: class MemoryPort: def __init__(self, adr, dat_r, we=None, dat_w=None, - async_read=False, re=None, we_granularity=0, mode=WRITE_FIRST): + async_read=False, re=None, we_granularity=0, mode=WRITE_FIRST, + clock_domain="sys"): self.adr = adr self.dat_r = dat_r self.we = we @@ -276,6 +301,7 @@ class MemoryPort: self.re = re self.we_granularity = we_granularity self.mode = mode + self.clock_domain = clock_domain class Memory: def __init__(self, width, depth, *ports, init=None): @@ -289,24 +315,66 @@ class Memory: class Fragment: def __init__(self, comb=None, sync=None, instances=None, memories=None, sim=None): if comb is None: comb = [] - if sync is None: sync = [] + if sync is None: sync = dict() if instances is None: instances = [] if memories is None: memories = [] if sim is None: sim = [] + + if isinstance(sync, list): + sync = {"sys": sync} + self.comb = comb self.sync = sync self.instances = instances self.memories = memories self.sim = sim + def __add__(self, other): - return Fragment(self.comb + other.comb, - self.sync + other.sync, + newsync = defaultdict(list) + for k, v in self.sync.items(): + newsync[k] = v[:] + for k, v in other.sync.items(): + newsync[k].extend(v) + return Fragment(self.comb + other.comb, newsync, self.instances + other.instances, self.memories + other.memories, self.sim + other.sim) + + def rename_clock_domain(self, old, new): + self.sync["new"] = self.sync["old"] + del self.sync["old"] + for inst in self.instances: + for cr in filter(lambda x: isinstance(x, Instance._CR), inst.items): + if cr.domain == old: + cr.domain = new + for mem in self.memories: + for port in mem.ports: + if port.clock_domain == old: + port.clock_domain = new + def get_clock_domains(self): + r = set(self.sync.keys()) + r |= set(cr.domain + for inst in self.instances + for cr in filter(lambda x: isinstance(x, Instance._CR), inst.items)) + r |= set(port.clock_domain + for mem in self.memories + for port in mem.ports) + return r + def call_sim(self, simulator): for s in self.sim: if simulator.cycle_counter >= 0 or (hasattr(s, "initialize") and s.initialize): s(simulator) + +class ClockDomain: + def __init__(self, n1, n2=None): + if n2 is None: + n_clk = n1 + "_clk" + n_rst = n1 + "_rst" + else: + n_clk = n1 + n_rst = n2 + self.clk = Signal(name_override=n_clk) + self.rst = Signal(name_override=n_rst) diff --git a/migen/fhdl/tools.py b/migen/fhdl/tools.py index a753d85e6..90ec62e52 100644 --- a/migen/fhdl/tools.py +++ b/migen/fhdl/tools.py @@ -31,7 +31,10 @@ def list_signals(node): l = list(map(lambda x: list_signals(x[1]), node.cases)) return list_signals(node.test).union(*l).union(list_signals(node.default)) elif isinstance(node, Fragment): - return list_signals(node.comb) | list_signals(node.sync) + l = list_signals(node.comb) + for k, v in node.sync.items(): + l |= list_signals(v) + return l else: raise TypeError @@ -56,7 +59,10 @@ def list_targets(node): l = list(map(lambda x: list_targets(x[1]), node.cases)) return list_targets(node.default).union(*l) elif isinstance(node, Fragment): - return list_targets(node.comb) | list_targets(node.sync) + l = list_targets(node.comb) + for k, v in node.sync.items(): + l |= list_targets(v) + return l else: raise TypeError @@ -78,16 +84,17 @@ def group_by_targets(sl): def list_inst_ios(i, ins, outs, inouts): if isinstance(i, Fragment): return list_inst_ios(i.instances, ins, outs, inouts) + elif isinstance(i, list): + if i: + return set.union(*(list_inst_ios(e, ins, outs, inouts) for e in i)) + else: + return set() else: - l = [] - for x in i: - if ins: - l += x.ins.values() - if outs: - l += x.outs.values() - if inouts: - l += x.inouts.values() - return set(l) + return set(item.signal for item in filter(lambda x: + (ins and isinstance(x, Instance.Input)) + or (outs and isinstance(x, Instance.Output)) + or (inouts and isinstance(x, Instance.InOut)), + i.items)) def list_mem_ios(m, ins, outs): if isinstance(m, Fragment): @@ -254,6 +261,10 @@ def _lower_arrays_sl(sl): def lower_arrays(f): f = copy(f) f.comb, ec1 = _lower_arrays_sl(f.comb) - f.sync, ec2 = _lower_arrays_sl(f.sync) - f.comb += ec1 + ec2 + f.comb += ec1 + newsync = dict() + for k, v in f.sync.items(): + newsync[k], ec2 = _lower_arrays_sl(v) + f.comb += ec2 + f.sync = newsync return f diff --git a/migen/fhdl/verilog.py b/migen/fhdl/verilog.py index c4bfbe154..034ac2c6e 100644 --- a/migen/fhdl/verilog.py +++ b/migen/fhdl/verilog.py @@ -169,57 +169,64 @@ def _printcomb(f, ns, display_run): r += "\n" return r -def _printsync(f, ns, clk, rst): +def _printsync(f, ns, clock_domains): r = "" - if f.sync: - r += "always @(posedge " + ns.get_name(clk) + ") begin\n" - r += _printnode(ns, _AT_SIGNAL, 1, insert_reset(rst, f.sync)) + for k, v in f.sync.items(): + r += "always @(posedge " + ns.get_name(clock_domains[k].clk) + ") begin\n" + r += _printnode(ns, _AT_SIGNAL, 1, insert_reset(clock_domains[k].rst, v)) r += "end\n\n" return r -def _printinstances(f, ns, clk, rst): +def _printinstances(f, ns, clock_domains): r = "" for x in f.instances: + parameters = list(filter(lambda i: isinstance(i, Instance.Parameter), x.items)) r += x.of + " " - if x.parameters: + if parameters: r += "#(\n" firstp = True - for p in x.parameters: + for p in parameters: if not firstp: r += ",\n" firstp = False - r += "\t." + p[0] + "(" - if isinstance(p[1], int) or isinstance(p[1], float) or isinstance(p[1], Constant): - r += str(p[1]) - elif isinstance(p[1], str): - r += "\"" + p[1] + "\"" + r += "\t." + p.name + "(" + if isinstance(p.value, int) or isinstance(p.value, float) or isinstance(p.value, Constant): + r += str(p.value) + elif isinstance(p.value, str): + r += "\"" + p.value + "\"" else: raise TypeError r += ")" r += "\n) " r += ns.get_name(x) - if x.parameters: r += " " + if parameters: r += " " r += "(\n" - ports = list(x.ins.items()) + list(x.outs.items()) + list(x.inouts.items()) - if x.clkport: - ports.append((x.clkport, clk)) - if x.rstport: - ports.append((x.rstport, rst)) firstp = True - for p in ports: + for p in x.items: + if isinstance(p, Instance._IO): + name_inst = p.name + name_design = ns.get_name(p.signal) + elif isinstance(p, Instance.ClockPort): + name_inst = p.name_inst + name_design = ns.get_name(clock_domains[p.domain].clk) + elif isinstance(p, Instance.ResetPort): + name_inst = p.name_inst + name_design = ns.get_name(clock_domains[p.domain].rst) + else: + continue if not firstp: r += ",\n" firstp = False - r += "\t." + p[0] + "(" + ns.get_name(p[1]) + ")" + r += "\t." + name_inst + "(" + name_design + ")" if not firstp: r += "\n" r += ");\n\n" return r -def _printmemories(f, ns, handler, clk): +def _printmemories(f, ns, handler, clock_domains): r = "" for memory in f.memories: - r += handler(memory, ns, clk) + r += handler(memory, ns, clock_domains) return r def _printinit(f, ios, ns): @@ -237,16 +244,17 @@ def _printinit(f, ios, ns): return r def convert(f, ios=set(), name="top", - clk_signal=None, rst_signal=None, + clock_domains=None, return_ns=False, memory_handler=verilog_mem_behavioral.handler, display_run=False): - if clk_signal is None: - clk_signal = Signal(name_override="sys_clk") - ios.add(clk_signal) - if rst_signal is None: - rst_signal = Signal(name_override="sys_rst") - ios.add(rst_signal) + if clock_domains is None: + clock_domains = dict() + for d in f.get_clock_domains(): + cd = ClockDomain(d) + clock_domains[d] = cd + ios.add(cd.clk) + ios.add(cd.rst) f = lower_arrays(f) @@ -258,9 +266,9 @@ def convert(f, ios=set(), name="top", r = "/* Machine-generated using Migen */\n" r += _printheader(f, ios, name, ns) r += _printcomb(f, ns, display_run) - r += _printsync(f, ns, clk_signal, rst_signal) - r += _printinstances(f, ns, clk_signal, rst_signal) - r += _printmemories(f, ns, memory_handler, clk_signal) + r += _printsync(f, ns, clock_domains) + r += _printinstances(f, ns, clock_domains) + r += _printmemories(f, ns, memory_handler, clock_domains) r += _printinit(f, ios, ns) r += "endmodule\n" diff --git a/migen/fhdl/verilog_mem_behavioral.py b/migen/fhdl/verilog_mem_behavioral.py index 59fb86f8e..a9c88e8cc 100644 --- a/migen/fhdl/verilog_mem_behavioral.py +++ b/migen/fhdl/verilog_mem_behavioral.py @@ -1,6 +1,6 @@ from migen.fhdl.structure import * -def handler(memory, ns, clk): +def handler(memory, ns, clock_domains): r = "" gn = ns.get_name adrbits = bits_for(memory.depth-1) @@ -24,8 +24,8 @@ def handler(memory, ns, clk): + gn(data_reg) + ";\n" data_regs[id(port)] = data_reg - r += "always @(posedge " + gn(clk) + ") begin\n" for port in memory.ports: + r += "always @(posedge " + gn(clock_domains[port.clock_domain].clk) + ") begin\n" if port.we is not None: if port.we_granularity: n = memory.width//port.we_granularity @@ -53,7 +53,7 @@ def handler(memory, ns, clk): else: r += "\tif (" + gn(port.re) + ")\n" r += "\t" + rd.replace("\n\t", "\n\t\t") - r += "end\n\n" + r += "end\n\n" for port in memory.ports: if port.async_read: diff --git a/migen/sim/generic.py b/migen/sim/generic.py index e027dc9c8..b4bd0ab0d 100644 --- a/migen/sim/generic.py +++ b/migen/sim/generic.py @@ -14,9 +14,14 @@ class TopLevel: self.top_name = top_name self.dut_type = dut_type self.dut_name = dut_name - self.clk_name = clk_name - self.clk_period = clk_period - self.rst_name = rst_name + + self._clk_name = clk_name + self._clk_period = clk_period + self._rst_name = rst_name + + cd = ClockDomain(self._clk_name, self._rst_name) + self.clock_domains = {"sys": cd} + self.ios = {cd.clk, cd.rst} def get(self, sockaddr): template1 = """`timescale 1ns / 1ps @@ -56,9 +61,9 @@ end r = template1.format(top_name=self.top_name, dut_type=self.dut_type, dut_name=self.dut_name, - clk_name=self.clk_name, - hclk_period=str(self.clk_period/2), - rst_name=self.rst_name, + clk_name=self._clk_name, + hclk_period=str(self._clk_period/2), + rst_name=self._rst_name, sockaddr=sockaddr) if self.vcd_name is not None: r += template2.format(vcd_name=self.vcd_name, @@ -78,13 +83,10 @@ class Simulator: c_top = self.top_level.get(sockaddr) - clk_signal = Signal(name_override=self.top_level.clk_name) - rst_signal = Signal(name_override=self.top_level.rst_name) c_fragment, self.namespace = verilog.convert(fragment, - {clk_signal, rst_signal}, + ios=self.top_level.ios, name=self.top_level.dut_type, - clk_signal=clk_signal, - rst_signal=rst_signal, + clock_domains=self.top_level.clock_domains, return_ns=True, **vopts) From f7b1e67d08c143996eaea2263c67b6b38e0e5ba6 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Mon, 10 Sep 2012 23:45:27 +0200 Subject: [PATCH 2/3] examples: update LM32 instance --- examples/basic/lm32_inst.py | 66 ++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/examples/basic/lm32_inst.py b/examples/basic/lm32_inst.py index 678dca557..2213f7802 100644 --- a/examples/basic/lm32_inst.py +++ b/examples/basic/lm32_inst.py @@ -4,36 +4,40 @@ from migen.fhdl import verilog class LM32: def __init__(self): self.inst = Instance("lm32_top", - [("I_ADR_O", BV(32)), - ("I_DAT_O", BV(32)), - ("I_SEL_O", BV(4)), - ("I_CYC_O", BV(1)), - ("I_STB_O", BV(1)), - ("I_WE_O", BV(1)), - ("I_CTI_O", BV(3)), - ("I_LOCK_O", BV(1)), - ("I_BTE_O", BV(1)), - ("D_ADR_O", BV(32)), - ("D_DAT_O", BV(32)), - ("D_SEL_O", BV(4)), - ("D_CYC_O", BV(1)), - ("D_STB_O", BV(1)), - ("D_WE_O", BV(1)), - ("D_CTI_O", BV(3)), - ("D_LOCK_O", BV(1)), - ("D_BTE_O", BV(1))], - [("interrupt", BV(32)), - ("ext_break", BV(1)), - ("I_DAT_I", BV(32)), - ("I_ACK_I", BV(1)), - ("I_ERR_I", BV(1)), - ("I_RTY_I", BV(1)), - ("D_DAT_I", BV(32)), - ("D_ACK_I", BV(1)), - ("D_ERR_I", BV(1)), - ("D_RTY_I", BV(1))], - clkport="clk_i", - rstport="rst_i", + Instance.ClockPort("clk_i"), + Instance.ResetPort("rst_i"), + + Instance.Input("interrupt", BV(32)), + Instance.Input("ext_break", BV(1)), + + Instance.Output("I_ADR_O", BV(32)), + Instance.Output("I_DAT_O", BV(32)), + Instance.Output("I_SEL_O", BV(4)), + Instance.Output("I_CYC_O", BV(1)), + Instance.Output("I_STB_O", BV(1)), + Instance.Output("I_WE_O", BV(1)), + Instance.Output("I_CTI_O", BV(3)), + Instance.Output("I_LOCK_O", BV(1)), + Instance.Output("I_BTE_O", BV(1)), + Instance.Input("I_DAT_I", BV(32)), + Instance.Input("I_ACK_I", BV(1)), + Instance.Input("I_ERR_I", BV(1)), + Instance.Input("I_RTY_I", BV(1)), + + Instance.Output("D_ADR_O", BV(32)), + Instance.Output("D_DAT_O", BV(32)), + Instance.Output("D_SEL_O", BV(4)), + Instance.Output("D_CYC_O", BV(1)), + Instance.Output("D_STB_O", BV(1)), + Instance.Output("D_WE_O", BV(1)), + Instance.Output("D_CTI_O", BV(3)), + Instance.Output("D_LOCK_O", BV(1)), + Instance.Output("D_BTE_O", BV(1)), + Instance.Input("D_DAT_I", BV(32)), + Instance.Input("D_ACK_I", BV(1)), + Instance.Input("D_ERR_I", BV(1)), + Instance.Input("D_RTY_I", BV(1)), + name="lm32") def get_fragment(self): @@ -43,4 +47,4 @@ cpus = [LM32() for i in range(4)] frag = Fragment() for cpu in cpus: frag += cpu.get_fragment() -print(verilog.convert(frag, set([cpus[0].inst.ins["interrupt"], cpus[0].inst.outs["I_WE_O"]]))) +print(verilog.convert(frag, set([cpus[0].inst.get_io("interrupt"), cpus[0].inst.get_io("I_WE_O")]))) From fc3187317b0ef60cc1510f4607f781aa8e8465c5 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Mon, 10 Sep 2012 23:46:19 +0200 Subject: [PATCH 3/3] examples: demonstrate multi-clock support --- examples/basic/memory.py | 2 +- examples/basic/psync.py | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 examples/basic/psync.py diff --git a/examples/basic/memory.py b/examples/basic/memory.py index ec61c83a9..30d0430cc 100644 --- a/examples/basic/memory.py +++ b/examples/basic/memory.py @@ -15,7 +15,7 @@ p1 = MemoryPort(a1, d1, we1, dw1, we_granularity=8) a2 = Signal(BV(d_b)) d2 = Signal(BV(w)) re2 = Signal() -p2 = MemoryPort(a2, d2, re=re2) +p2 = MemoryPort(a2, d2, re=re2, clock_domain="rd") mem = Memory(w, d, p1, p2, init=[5, 18, 32]) f = Fragment(memories=[mem]) diff --git a/examples/basic/psync.py b/examples/basic/psync.py new file mode 100644 index 000000000..4ca25f5d1 --- /dev/null +++ b/examples/basic/psync.py @@ -0,0 +1,23 @@ +from migen.fhdl.structure import * +from migen.fhdl import verilog + +# convert pulse into level change +i = Signal() +level = Signal() +isync = [If(i, level.eq(~level))] + +# synchronize level to oclk domain +slevel = [Signal() for i in range(3)] +osync = [ + slevel[0].eq(level), + slevel[1].eq(slevel[0]), + slevel[2].eq(slevel[1]) +] + +# regenerate pulse +o = Signal() +comb = [o.eq(slevel[1] ^ slevel[2])] + +f = Fragment(comb, {"i": isync, "o": osync}) +v = verilog.convert(f, ios={i, o}) +print(v)