2 from soc
.fu
.div
.fsm
import DivState
, DivStateInit
, DivStateNext
3 from nmigen
import Elaboratable
, Module
, Signal
, unsigned
4 from nmigen
.cli
import rtlil
6 from nmigen
.sim
.pysim
import Simulator
, Delay
, Tick
8 from nmigen
.back
.pysim
import Simulator
, Delay
, Tick
11 class CheckEvent(Elaboratable
):
12 """helper to add indication to vcd when signals are checked
19 yield self
.event
.eq(~self
.event
)
21 def elaborate(self
, platform
):
23 # use event somehow so nmigen simulation knows about it
24 m
.d
.comb
+= Signal().eq(self
.event
)
28 class DivStateCombTest(Elaboratable
):
29 """Test stringing a bunch of copies of the FSM state-function together
32 def __init__(self
, quotient_width
):
33 self
.check_event
= CheckEvent()
34 self
.quotient_width
= quotient_width
35 self
.dividend
= Signal(unsigned(quotient_width
* 2))
36 self
.divisor
= Signal(unsigned(quotient_width
))
37 self
.quotient
= Signal(unsigned(quotient_width
))
38 self
.remainder
= Signal(unsigned(quotient_width
))
39 self
.expected_quotient
= Signal(unsigned(quotient_width
))
40 self
.expected_remainder
= Signal(unsigned(quotient_width
))
41 self
.expected_valid
= Signal()
43 for i
in range(quotient_width
+ 1):
44 state
= DivState(quotient_width
=quotient_width
, name
=f
"state{i}")
45 self
.states
.append(state
)
46 self
.init
= DivStateInit(quotient_width
)
48 for i
in range(quotient_width
):
49 next
= DivStateNext(quotient_width
)
50 self
.nexts
.append(next
)
52 def elaborate(self
, platform
):
54 m
.submodules
.check_event
= self
.check_event
55 m
.submodules
.init
= self
.init
56 m
.d
.comb
+= self
.init
.dividend
.eq(self
.dividend
)
57 m
.d
.comb
+= self
.states
[0].eq(self
.init
.o
)
58 last_state
= self
.states
[0]
59 for i
in range(self
.quotient_width
):
60 setattr(m
.submodules
, f
"next{i}", self
.nexts
[i
])
61 m
.d
.comb
+= self
.nexts
[i
].divisor
.eq(self
.divisor
)
62 m
.d
.comb
+= self
.nexts
[i
].i
.eq(last_state
)
63 last_state
= self
.states
[i
+ 1]
64 m
.d
.comb
+= last_state
.eq(self
.nexts
[i
].o
)
65 m
.d
.comb
+= self
.quotient
.eq(last_state
.quotient
)
66 m
.d
.comb
+= self
.remainder
.eq(last_state
.remainder
)
67 m
.d
.comb
+= self
.expected_valid
.eq(
68 (self
.dividend
< (self
.divisor
<< self
.quotient_width
))
69 & (self
.divisor
!= 0))
70 with m
.If(self
.expected_valid
):
71 m
.d
.comb
+= self
.expected_quotient
.eq(
72 self
.dividend
// self
.divisor
)
73 m
.d
.comb
+= self
.expected_remainder
.eq(
74 self
.dividend
% self
.divisor
)
78 class DivStateFSMTest(Elaboratable
):
79 def __init__(self
, quotient_width
):
80 self
.check_done_event
= CheckEvent()
81 self
.check_event
= CheckEvent()
82 self
.quotient_width
= quotient_width
83 self
.dividend
= Signal(unsigned(quotient_width
* 2))
84 self
.divisor
= Signal(unsigned(quotient_width
))
85 self
.quotient
= Signal(unsigned(quotient_width
))
86 self
.remainder
= Signal(unsigned(quotient_width
))
87 self
.expected_quotient
= Signal(unsigned(quotient_width
))
88 self
.expected_remainder
= Signal(unsigned(quotient_width
))
89 self
.expected_valid
= Signal()
90 self
.state
= DivState(quotient_width
=quotient_width
,
92 self
.next_state
= DivState(quotient_width
=quotient_width
,
94 self
.init
= DivStateInit(quotient_width
)
95 self
.next
= DivStateNext(quotient_width
)
96 self
.state_done
= Signal()
97 self
.next_state_done
= Signal()
98 self
.clear
= Signal(reset
=1)
100 def elaborate(self
, platform
):
102 m
.submodules
.check_event
= self
.check_event
103 m
.submodules
.check_done_event
= self
.check_done_event
104 m
.submodules
.init
= self
.init
105 m
.submodules
.next
= self
.next
106 m
.d
.comb
+= self
.init
.dividend
.eq(self
.dividend
)
107 m
.d
.comb
+= self
.next
.divisor
.eq(self
.divisor
)
108 m
.d
.comb
+= self
.quotient
.eq(self
.state
.quotient
)
109 m
.d
.comb
+= self
.remainder
.eq(self
.state
.remainder
)
110 m
.d
.comb
+= self
.next
.i
.eq(self
.state
)
111 m
.d
.comb
+= self
.state_done
.eq(self
.state
.done
)
112 m
.d
.comb
+= self
.next_state_done
.eq(self
.next_state
.done
)
114 with m
.If(self
.state
.done | self
.clear
):
115 m
.d
.comb
+= self
.next_state
.eq(self
.init
.o
)
117 m
.d
.comb
+= self
.next_state
.eq(self
.next
.o
)
119 m
.d
.sync
+= self
.state
.eq(self
.next_state
)
121 m
.d
.comb
+= self
.expected_valid
.eq(
122 (self
.dividend
< (self
.divisor
<< self
.quotient_width
))
123 & (self
.divisor
!= 0))
124 with m
.If(self
.expected_valid
):
125 m
.d
.comb
+= self
.expected_quotient
.eq(
126 self
.dividend
// self
.divisor
)
127 m
.d
.comb
+= self
.expected_remainder
.eq(
128 self
.dividend
% self
.divisor
)
132 def get_cases(quotient_width
):
134 mask
= ~
(~
0 << quotient_width
)
135 for i
in range(-3, 4):
136 test_cases
.append(i
& mask
)
138 test_cases
.append((i
+ (mask
>> 1)) & mask
)
143 class TestDivState(unittest
.TestCase
):
144 def test_div_state_comb(self
, quotient_width
=8):
145 test_cases
= get_cases(quotient_width
)
146 mask
= ~
(~
0 << quotient_width
)
147 dut
= DivStateCombTest(quotient_width
)
148 vl
= rtlil
.convert(dut
,
153 with
open("div_fsm_comb_pipeline.il", "w") as f
:
155 dut
= DivStateCombTest(quotient_width
)
157 def check(dividend
, divisor
):
158 with self
.subTest(dividend
=f
"{dividend:#x}",
159 divisor
=f
"{divisor:#x}"):
160 yield from dut
.check_event
.trigger()
161 for i
in range(quotient_width
+ 1):
162 # done must be correct and eventually true
163 # even if a div-by-zero or overflow occurred
164 done
= yield dut
.states
[i
].done
165 self
.assertEqual(done
, i
== quotient_width
)
167 quotient
= dividend
// divisor
168 remainder
= dividend
% divisor
170 with self
.subTest(quotient
=f
"{quotient:#x}",
171 remainder
=f
"{remainder:#x}"):
172 self
.assertTrue((yield dut
.expected_valid
))
173 self
.assertEqual((yield dut
.expected_quotient
),
175 self
.assertEqual((yield dut
.expected_remainder
),
177 self
.assertEqual((yield dut
.quotient
), quotient
)
178 self
.assertEqual((yield dut
.remainder
), remainder
)
180 self
.assertFalse((yield dut
.expected_valid
))
182 self
.assertFalse((yield dut
.expected_valid
))
185 for dividend_high
in test_cases
:
186 for dividend_low
in test_cases
:
187 dividend
= dividend_low
+ \
188 (dividend_high
<< quotient_width
)
189 for divisor
in test_cases
:
192 yield dut
.dividend
.eq(dividend
)
193 yield dut
.divisor
.eq(divisor
)
197 yield from check(dividend
, divisor
)
200 yield from process(gen
=True)
203 yield from process(gen
=False)
206 with sim
.write_vcd(vcd_file
="div_fsm_comb_pipeline.vcd",
207 gtkw_file
="div_fsm_comb_pipeline.gtkw"):
209 sim
.add_process(gen_process
)
210 sim
.add_process(check_process
)
213 def test_div_state_fsm(self
, quotient_width
=8):
214 test_cases
= get_cases(quotient_width
)
215 mask
= ~
(~
0 << quotient_width
)
216 dut
= DivStateFSMTest(quotient_width
)
217 vl
= rtlil
.convert(dut
,
222 with
open("div_fsm.il", "w") as f
:
225 def check(dividend
, divisor
):
226 with self
.subTest(dividend
=f
"{dividend:#x}",
227 divisor
=f
"{divisor:#x}"):
228 for i
in range(quotient_width
+ 1):
231 yield from dut
.check_done_event
.trigger()
233 # done must be correct and eventually true
234 # even if a div-by-zero or overflow occurred
235 done
= yield dut
.state
.done
236 self
.assertEqual(done
, i
== quotient_width
)
237 yield from dut
.check_event
.trigger()
240 # FIXME(programmerjake): replace with public API
241 # see https://github.com/nmigen/nmigen/issues/443
242 now
= sim
._engine
.now
243 except AttributeError:
246 quotient
= dividend
// divisor
247 remainder
= dividend
% divisor
249 with self
.subTest(quotient
=f
"{quotient:#x}",
250 remainder
=f
"{remainder:#x}",
252 self
.assertTrue((yield dut
.expected_valid
))
253 self
.assertEqual((yield dut
.expected_quotient
),
255 self
.assertEqual((yield dut
.expected_remainder
),
257 self
.assertEqual((yield dut
.quotient
), quotient
)
258 self
.assertEqual((yield dut
.remainder
), remainder
)
260 self
.assertFalse((yield dut
.expected_valid
))
262 self
.assertFalse((yield dut
.expected_valid
))
266 yield dut
.clear
.eq(1)
269 yield from dut
.check_event
.trigger()
270 yield from dut
.check_done_event
.trigger()
271 for dividend_high
in test_cases
:
272 for dividend_low
in test_cases
:
273 dividend
= dividend_low
+ \
274 (dividend_high
<< quotient_width
)
275 for divisor
in test_cases
:
278 yield dut
.clear
.eq(0)
279 yield dut
.dividend
.eq(dividend
)
280 yield dut
.divisor
.eq(divisor
)
281 for _
in range(quotient_width
+ 1):
284 yield from check(dividend
, divisor
)
287 yield from process(gen
=True)
290 yield from process(gen
=False)
293 with sim
.write_vcd(vcd_file
="div_fsm.vcd",
294 gtkw_file
="div_fsm.gtkw"):
297 sim
.add_process(gen_process
)
298 sim
.add_process(check_process
)
302 if __name__
== "__main__":