1 """Simple example of a FSM-based ALU
3 This demonstrates a design that follows the valid/ready protocol of the
4 ALU, but with a FSM implementation, instead of a pipeline. It is also
5 intended to comply with both the CompALU API and the nmutil Pipeline API
6 (Liskov Substitution Principle)
10 1) p.ready_o is asserted on the initial ("Idle") state, otherwise it keeps low.
11 2) n.valid_o is asserted on the final ("Done") state, otherwise it keeps low.
12 3) The FSM stays in the Idle state while p.valid_i is low, otherwise
13 it accepts the input data and moves on.
14 4) The FSM stays in the Done state while n.ready_i is low, otherwise
15 it releases the output data and goes back to the Idle state.
19 from nmigen
import Elaboratable
, Signal
, Module
, Cat
20 from nmigen
.back
.pysim
import Simulator
21 from nmigen
.cli
import rtlil
22 from soc
.fu
.cr
.cr_input_record
import CompCROpSubset
29 class Shifter(Elaboratable
):
30 """Simple sequential shifter
33 * p.data_i.data: value to be shifted
34 * p.data_i.shift: shift amount
35 * When zero, no shift occurs.
36 * On POWER, range is 0 to 63 for 32-bit,
37 * and 0 to 127 for 64-bit.
38 * Other values wrap around.
39 * p.data_i.dir: shift direction (0 = left, 1 = right)
42 * n.data_o.data: shifted value
45 def __init__(self
, width
):
46 self
.data
= Signal(width
, name
="p_data_i")
47 self
.shift
= Signal(width
, name
="p_shift_i")
48 self
.dir = Signal(name
="p_dir_i")
49 self
.ctx
= Dummy() # comply with CompALU API
52 return [self
.data
, self
.shift
]
55 def __init__(self
, width
):
56 self
.data
= Signal(width
, name
="n_data_o")
62 def __init__(self
, width
):
63 self
.data_i
= Shifter
.PrevData(width
)
64 self
.valid_i
= Signal(name
="p_valid_i")
65 self
.ready_o
= Signal(name
="p_ready_o")
68 def __init__(self
, width
):
69 self
.data_o
= Shifter
.NextData(width
)
70 self
.valid_o
= Signal(name
="n_valid_o")
71 self
.ready_i
= Signal(name
="n_ready_i")
73 def __init__(self
, width
):
75 self
.p
= self
.PrevPort(width
)
76 self
.n
= self
.NextPort(width
)
78 # more pieces to make this example class comply with the CompALU API
79 self
.op
= CompCROpSubset()
80 self
.p
.data_i
.ctx
.op
= self
.op
81 self
.i
= self
.p
.data_i
._get
_data
()
82 self
.out
= self
.n
.data_o
._get
_data
()
84 def elaborate(self
, platform
):
88 # It is good practice to design a sequential circuit as
89 # a data path and a control path.
93 # The idea is to have a register that can be
94 # loaded or shifted (left and right).
100 shift_in
= Signal(self
.width
)
101 shift_left_by_1
= Signal(self
.width
)
102 shift_right_by_1
= Signal(self
.width
)
103 next_shift
= Signal(self
.width
)
105 shift_reg
= Signal(self
.width
, reset_less
=True)
106 # build the data flow
108 # connect input and output
109 shift_in
.eq(self
.p
.data_i
.data
),
110 self
.n
.data_o
.data
.eq(shift_reg
),
111 # generate shifted views of the register
112 shift_left_by_1
.eq(Cat(0, shift_reg
[:-1])),
113 shift_right_by_1
.eq(Cat(shift_reg
[1:], 0)),
115 # choose the next value of the register according to the
117 # default is no change
118 m
.d
.comb
+= next_shift
.eq(shift_reg
)
120 m
.d
.comb
+= next_shift
.eq(shift_in
)
122 with m
.If(self
.p
.data_i
.dir):
123 m
.d
.comb
+= next_shift
.eq(shift_right_by_1
)
125 m
.d
.comb
+= next_shift
.eq(shift_left_by_1
)
127 # register the next value
128 m
.d
.sync
+= shift_reg
.eq(next_shift
)
130 # TODO: Implement the control path
135 yield self
.p
.data_i
.data
136 yield self
.p
.data_i
.shift
137 yield self
.p
.data_i
.dir
142 yield self
.n
.data_o
.data
150 m
.submodules
.shf
= dut
= Shifter(8)
151 print("Shifter port names:")
153 print("-", port
.name
)
155 # try "proc; show" in yosys to check the data path
156 il
= rtlil
.convert(dut
, ports
=dut
.ports())
157 with
open("test_shifter.il", "w") as f
:
160 # Todo: Implement Simulation
163 if __name__
== "__main__":