1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
|
# general imports
import sys
from inspect import stack # get name of caller function
from typing import Callable
from math import log2, ceil
from enum import Enum
import random
#hdl specific imports
from amaranth import *
from amaranth import Elaboratable
from amaranth.back import verilog, cxxrtl
from amaranth.sim import Settle, Delay, Simulator
#custom imports
from hdl.config import *
# configuration
random.seed(5498)
__all__ = ['cmd', 'e2s', 'sim', 'step', 'eval', 'rand_bits', 'rand_bits_extremes', 'rand_bits_mix']
def cmd(hdl):
'''
Very simple command line interface
The elaboratable class must have a ports attribute that is a dict of in and out ports {'in': [Signals()], 'out': [Signals()]}
'''
if len(sys.argv) <= 1:
print('Usage: v|cc v = generate verilog, cc = generate cxxrtl')
exit()
if sys.argv[1] == "sim":
# tb(sys.argv[0].replace('.py', '.vcd'))
# exit()
assert "sim option deprecated, use pytest command instead"
if sys.argv[1] == "v":
out = verilog.convert(hdl, ports=hdl.ports['in'] + hdl.ports['out'])
with open(os.path.join(VERILOG_DIR, os.path.basename(sys.argv[0]).replace('.py', '.v')), 'w') as f:
f.write(out)
elif sys.argv[1] == "cc":
out = cxxrtl.convert(hdl, ports=hdl.ports['in'] + hdl.ports['out'])
with open(os.path.join(CXXRTL_DIR, os.path.basename(sys.argv[0]).replace('.py', '.cc')), 'w') as f:
f.write(out)
def e2s(e: Enum):
'''
Get signal length from enum, returns ceil(log2(len(e))))
'''
return ceil(log2(len(e)))
def sim(dut:Elaboratable, proc: Callable):
sim = Simulator(dut)
sim.add_clock(1e-6)
sim.add_sync_process(proc)
with sim.write_vcd(os.path.join(VCD_DIR, stack()[1].function + '.vcd')): # get name of caller function
sim.run()
def step(cycles=1):
for _ in range(cycles):
yield Settle() # settle comb logic before clock
yield # clock edge
yield Settle() # settle comb logic after clock
yield Delay(5e-7) # used for debugging, change values on neg edge of clock
def eval():
yield Settle()
yield Delay(1e-6)
# bits can be integer or Signal
def rand_bits(bits, sus=None, low=None, high=None):
'''
Takes number of bits or a Signal and returns a random number
if low and high are not specified, the range is 0 to 2**bits - 1
If low and high are specified the number of bits is ignored
sus (Signed UnSigned) can be 's' or 'u' to force signed or unsigned, default is random
sus has no effect if low and high are specified
'''
if isinstance(bits, Signal):
bits = bits.width
if (low == high == None):
if sus == 's':
low = -2**(bits-1)
high = 2**(bits-1) - 1
elif sus == 'u':
low = 0
high = 2**bits - 1
else:
if random.random() < 0.5:
low = 0
high = 2**bits - 1
else:
low = -2**(bits-1)
high = 2**(bits-1) - 1
else:
assert low < high, "low must be less than high"
assert low != None, "low must be defined if high is defined"
assert high != None, "high must be defined if low is defined"
return random.randint(low, high)
# bits can be integer or Signal
def rand_bits_extremes(bits, sus=None):
'''
Takes number of bits or a Signal and returns a random number with the extremes of the range
sus (Signed UnSigned) can be 's' or 'u' to force signed or unsigned, default is random
'''
if isinstance(bits, Signal):
bits = bits.width
if sus == 's':
low = -2**(bits-1)
high = 2**(bits-1) - 1
elif sus == 'u':
low = 0
high = 2**bits - 1
else:
if random.random() < 0.5:
low = 0
high = 2**bits - 1
else:
low = -2**(bits-1)
high = 2**(bits-1) - 1
choices = [low, high] if low == 0 else [low, 0, high] # if low is 0, don't include it as a choice
return random.choice(choices)
def rand_bits_mix(bits, low=None, high=None, sus=None, extrem_prob=0.25):
'''
Take number of bits or a Signal
sus determines if the number is signed or unsigned, or both ('s', 'u', or None)
Returns a random number with increased likelihood of extremes
If low and high are specified the number of bits is ignored
The likelyhood of extremes is determined by extrem_prob, default is 0.25
'''
if random.random() <= extrem_prob:
return rand_bits_extremes(bits, sus=sus)
else:
return rand_bits(bits, sus=sus, low=low, high=high)
|