add CLMulAdd and tests
[nmutil.git] / src / nmutil / util.py
1 """
2 This work is funded through NLnet under Grant 2019-02-012
3
4 License: LGPLv3+
5
6 """
7
8 from collections.abc import Iterable
9 from nmigen import Mux, Signal, Cat
10
11 # XXX this already exists in nmigen._utils
12 # see https://bugs.libre-soc.org/show_bug.cgi?id=297
13 def flatten(v):
14 if isinstance(v, Iterable):
15 for i in v:
16 yield from flatten(i)
17 else:
18 yield v
19
20 # tree reduction function. operates recursively.
21 def treereduce(tree, op, fn):
22 """treereduce: apply a map-reduce to a list.
23 examples: OR-reduction of one member of a list of Records down to a
24 single data point:
25 treereduce(tree, operator.or_, lambda x: getattr(x, "o_data"))
26 """
27 #print ("treereduce", tree)
28 if not isinstance(tree, list):
29 return tree
30 if len(tree) == 1:
31 return fn(tree[0])
32 if len(tree) == 2:
33 return op(fn(tree[0]), fn(tree[1]))
34 s = len(tree) // 2 # splitpoint
35 return op(treereduce(tree[:s], op, fn),
36 treereduce(tree[s:], op, fn))
37
38 # chooses assignment of 32 bit or full 64 bit depending on is_32bit
39 def eq32(is_32bit, dest, src):
40 return [dest[0:32].eq(src[0:32]),
41 dest[32:64].eq(Mux(is_32bit, 0, src[32:64]))]
42
43
44 # a wrapper function formerly in run_simulation that is still useful.
45 # Simulation.add_sync_process now only takes functions, it does not
46 # take generators. so passing in arguments is no longer possible.
47 # with this wrapper, the following is possible:
48 # sim.add_sync_process(wrap.dut(parallel_sender_number=0))
49 # sim.add_sync_process(wrap.dut(parallel_sender_number=1))
50
51 def wrap(process):
52 def wrapper():
53 yield from process
54 return wrapper
55
56
57 # a "rising edge" generator. can take signals of greater than width 1
58
59 def rising_edge(m, sig):
60 delay = Signal.like(sig)
61 rising = Signal.like(sig)
62 delay.name = "%s_dly" % sig.name
63 rising.name = "%s_rise" % sig.name
64 m.d.sync += delay.eq(sig) # 1 clock delay
65 m.d.comb += rising.eq(sig & ~delay) # sig is hi but delay-sig is lo
66 return rising
67
68
69 # Display function (dummy if non-existent)
70 # added as a patch from jeanthom
71 # https://gist.githubusercontent.com/jeanthom/
72 # f97f5b928720d4adda9d295e8a5bc078/
73 # raw/694274e0aceec993c0fc127e296b1a85b93c1b89/nmigen-display.diff
74 try:
75 from nmigen.hdl.ast import Display
76 except ImportError:
77 def Display(*args):
78 return []
79
80
81 def sel(m, r, sel_bits, field_width=None, name=None, src_loc_at=0):
82 """Forms a subfield from a selection of bits of the signal `r`
83 ("register").
84
85 :param m: nMigen Module for adding the wires
86 :param r: signal containing the field from which to select the subfield
87 :param sel_bits: bit indices of the subfield, in "MSB 0" convention,
88 from most significant to least significant. Note that
89 the indices are allowed to be non-contiguous and/or
90 out-of-order.
91 :param field_width: field width. If absent, use the signal `r` own width.
92 :param name: name of the generated Signal
93 :param src_loc_at: in the absence of `name`, stack level in which
94 to find it
95
96 :returns: a new Signal which gets assigned to the subfield
97 """
98 # find the MSB index in LSB0 numbering
99 if field_width is None:
100 msb = len(r) - 1
101 else:
102 msb = field_width - 1
103 # extract the selected bits
104 sig_list = []
105 for idx in sel_bits:
106 sig_list.append(r[msb - idx])
107 # place the LSB at the front of the list,
108 # since, in nMigen, Cat starts from the LSB
109 sig_list.reverse()
110 sel_ret = Signal(len(sig_list), name=name, src_loc_at=src_loc_at+1)
111 m.d.comb += sel_ret.eq(Cat(*sig_list))
112 return sel_ret