Skip to content
This repository was archived by the owner on Jan 5, 2026. It is now read-only.

Commit 1fb3a5a

Browse files
committed
examples/sim: add shift register examples
2 types of shift register implementations are introduced. * `sr1.py`: Implementation of shift-right register that begins shifting at reset. It is demonstrated with a parallel-to-serial and a serial-to-parallel register, each of which processes 8 bits of data at a time. * `sr2.py`: Implementation of shift-right register whose shifting action is controlled by an input and an output strobe. It is demonstrated with a parallel-to-serial and a serial-to-parallel register, each of which can output 8 bits of data while fetching in new 8-bit data simultaneously.
1 parent 7bc4eb1 commit 1fb3a5a

2 files changed

Lines changed: 447 additions & 0 deletions

File tree

examples/sim/sr1.py

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
from migen import *
2+
3+
4+
class SimpleShiftRegister(Module):
5+
"""Base implementation of a simple shift-right register.
6+
7+
At every clock cycle, the input data ('i') will be written in (shifted in) in the next clock cycle, while the output data will be read out (shifted out) in the next clock cycle.
8+
9+
The inner shift register (`sr`) has the size of (input width + data width). This register contains a buffer, which allows storing a new data value while shifting out the current data. At all times, `sr` needs to store exactly 1 whole data and all bits of the data to be shifted out.
10+
"""
11+
def __init__(self, data_width, input_width, output_width):
12+
assert data_width >= input_width
13+
assert data_width >= output_width
14+
15+
self.i = Signal(input_width)
16+
self.o = Signal(output_width)
17+
18+
self._sr = sr = Signal(input_width + data_width)
19+
self._counter = counter = (Signal(max=input_width) if input_width>1
20+
else Signal())
21+
22+
self.sync += [
23+
If(counter == 0,
24+
sr.eq(Cat(sr[1:output_width], self.i))
25+
).Else(
26+
sr[:-1].eq(sr[1:])
27+
),
28+
If(counter == input_width - 1,
29+
counter.eq(0)
30+
).Else(
31+
counter.eq(counter + 1)
32+
)
33+
]
34+
self.comb += self.o.eq(sr[0:output_width])
35+
36+
37+
class SimpleShiftRegister_ParallelToSerial(SimpleShiftRegister):
38+
"""A parallel-to-serial shift-right register.
39+
40+
All bits of the new value are shifted in only right before all current bits are shifted out.
41+
"""
42+
def __init__(self, data_width=8):
43+
super().__init__(data_width=data_width, input_width=data_width, output_width=1)
44+
45+
46+
class SimpleShiftRegister_SerialToParallel(SimpleShiftRegister):
47+
"""A serial-to-parallel shift-right register.
48+
49+
Shifting out all bits of the current value is immediately followed byshifting in the first bit of the new value.
50+
"""
51+
def __init__(self, data_width=8):
52+
super().__init__(data_width=data_width, input_width=1, output_width=data_width)
53+
self.o_valid = Signal()
54+
55+
o_counter = Signal(max=data_width)
56+
57+
self.sync += [
58+
If(o_counter == data_width - 1,
59+
o_counter.eq(0)
60+
).Else(
61+
o_counter.eq(o_counter + 1)
62+
)
63+
]
64+
self.comb += self.o_valid.eq(o_counter == 0)
65+
66+
67+
def flipping_test(dut, init_value):
68+
assert init_value <= 0b11111111
69+
70+
is_p2s = isinstance(dut, SimpleShiftRegister_ParallelToSerial)
71+
is_s2p = isinstance(dut, SimpleShiftRegister_SerialToParallel)
72+
73+
init_value_flipped = ~init_value & 0b11111111
74+
data = [0 for _ in range(8)] # reset value
75+
data_old = [(init_value >> i) & 1 for i in range(8)]
76+
data_new = [(init_value_flipped >> i) & 1 for i in range(8)]
77+
# For serial-in test: store the "next bit" index
78+
next_ind = 1
79+
80+
# Assume t is a timer with a reset of 0
81+
for t in range(26):
82+
# Assign the init_value to input when t=4, flip the bits when t=16, and flip them back to the original value when t=21
83+
if t in (3, 20):
84+
data = data_old[:]
85+
if t == 15:
86+
data = data_new[:]
87+
# For parallel-in:
88+
# - Assign input with all data bits as input all the time
89+
if is_p2s:
90+
for ind in range(8):
91+
yield dut.i[ind].eq(data[ind])
92+
# For serial-in:
93+
# - Enable input all the time
94+
# - Move the next bit of the up-to-date data into the input
95+
elif is_s2p:
96+
yield dut.i.eq(data[next_ind])
97+
# Print the output for each cycle
98+
if is_p2s:
99+
print("t={:>2} : i == {}{}{}{}{}{}{}{}, o == {}"
100+
.format(t,
101+
(yield dut.i[7]), (yield dut.i[6]), (yield dut.i[5]),
102+
(yield dut.i[4]), (yield dut.i[3]), (yield dut.i[2]),
103+
(yield dut.i[1]), (yield dut.i[0]),
104+
(yield dut.o)),
105+
("[input = {:08b}]".format(init_value) if t == 4 else
106+
"[output = {:08b}]".format(0) if t == 8 else
107+
"[output = {:08b}, and input = {:08b}]".format(init_value, init_value_flipped) if t == 16 else
108+
"[input = {:08b}]".format(init_value) if t == 21 else
109+
"[output = {:08b}]".format(init_value_flipped) if t == 24 else
110+
"[reset, input = {:08b}]".format(0) if t == 0 else ""))
111+
elif is_s2p:
112+
print("t={:>2} : i == {}, o(valid={}) == {}{}{}{}{}{}{}{}"
113+
.format(t, (yield dut.i), (yield dut.o_valid),
114+
(yield dut.o[7]), (yield dut.o[6]), (yield dut.o[5]),
115+
(yield dut.o[4]), (yield dut.o[3]), (yield dut.o[2]),
116+
(yield dut.o[1]), (yield dut.o[0])),
117+
("[input = {:04b}....]".format(init_value >> 4) if t == 4 else
118+
"[valid output]" if t == 8 else
119+
"[valid output, and input = ...{:05b}]".format(init_value_flipped & 0b11111) if t == 16 else
120+
"[input = {:03b}....]".format(init_value >> 5) if t == 21 else
121+
"[valid output]" if t == 24 else
122+
"[reset, input = {:08b}]".format(0) if t == 0 else ""))
123+
# When 1<=t<=4:
124+
# - For serial-out: output (bit 0 to 3) should correspond to the reset data value (i.e. 0x00)
125+
# - For serial-in: output is invalid; bit 3 is the newest bit (of the reset value) shifted in
126+
if t in range(1, 5):
127+
if is_p2s:
128+
assert (yield dut.o) == 0
129+
elif is_s2p:
130+
assert not (yield dut.o_valid)
131+
# When 5<=t<=8:
132+
# - For serial-out: although the first value has been inputted at t=6, the output (bit 4 to 7) should still correspond to the reset value, since it hasn't been fully shifted out
133+
# - For serial-in:
134+
# - When t=5: the initial value gets assigned as input starting at t=5, so bit 4 from the first value has been shifted in
135+
# - When 6<=t=7: bits 5 and 6 from the first value has been shifted in
136+
# - When t=8: bit 7 has been shifted in so output is valid and should correspond to: bits 0 to 3 of the reset value (0b000), and bits 4 to 7 of the first value
137+
if t in range(5, 9):
138+
if is_p2s:
139+
assert (yield dut.o) == 0
140+
elif is_s2p:
141+
assert (not (yield dut.o_valid)) != (t == 8)
142+
if (yield dut.o_valid):
143+
for i in range(0, 4):
144+
assert (yield dut.o[i]) == 0
145+
for i in range(4, 7):
146+
assert (yield dut.o[i]) == data_old[i]
147+
# When 9<=t<=16:
148+
# - For serial-out: output should correspond to the first data value
149+
# - For serial-in:
150+
# - When 9<=t=15: bits 0 and 6 from the first value has been shifted in
151+
# - When t=16: bit 7 has been shifted in so output is valid and should correspond to the first data value
152+
if t in range(9, 17):
153+
if is_p2s:
154+
assert (yield dut.o) == data_old[t-9]
155+
elif is_s2p:
156+
assert (not (yield dut.o_valid)) != (t == 16)
157+
if (yield dut.o_valid):
158+
for i in range(0, 7):
159+
assert (yield dut.o[i]) == data_old[i]
160+
# When 17<=t<=21:
161+
# - For serial-out: output (bit 0 to 4) should correspond to the second data value
162+
# - For serial-in: output is invalid; bit 4 is the newest bit shifted in
163+
if t in range(17, 22):
164+
if is_p2s:
165+
assert (yield dut.o) == data_new[t-17]
166+
elif is_s2p:
167+
assert not (yield dut.o_valid)
168+
# When 22<=t<=24:
169+
# - For serial-out: although the first value has been inputted at t=22, the output (bit 5 to 7) should still correspond to the second data value, since it hasn't been fully shifted out
170+
# - For serial-in:
171+
# - When t=22: new value gets flipped back to the original one starting at t=22, so bit 5 from the original value has been shifted in
172+
# - When t=23: bit 6 from the original value has been shifted in
173+
# - When t=24: bit 7 has been shifted in so output is valid and should correspond to: bits 0 to 4 of the second value, and bits 5 to 7 of the first value
174+
if t in range(22, 25):
175+
if is_p2s:
176+
assert (yield dut.o) == data_new[t-22+5]
177+
elif is_s2p:
178+
assert (not (yield dut.o_valid)) != (t == 24)
179+
if (yield dut.o_valid):
180+
for i in range(0, 5):
181+
assert (yield dut.o[i]) == data_new[i]
182+
for i in range(5, 7):
183+
assert (yield dut.o[i]) == data_old[i]
184+
# When t = 25:
185+
# - For serial-out: output (bit 0) should correspond to the first value
186+
# - For serial-in: output is invalid
187+
if t == 25:
188+
if is_p2s:
189+
assert (yield dut.o) == data_old[t-25]
190+
elif is_s2p:
191+
assert not (yield dut.o_valid)
192+
yield
193+
# For serial-in: always update the "next bit" index
194+
next_ind = (next_ind + 1) % 8
195+
196+
197+
if __name__ == "__main__":
198+
print("\n** Parallel-in, Serial-out SR Test **")
199+
dut = SimpleShiftRegister_ParallelToSerial()
200+
run_simulation(dut, flipping_test(dut, 0b10101010), vcd_name="sr1_p2s.vcd")
201+
print("\n** Serial-in, Parallel-out SR Test **")
202+
dut = SimpleShiftRegister_SerialToParallel()
203+
run_simulation(dut, flipping_test(dut, 0b01010101), vcd_name="sr1_s2p.vcd")

0 commit comments

Comments
 (0)