The basic rules are:
-1) p.ready_o is asserted on the initial ("Idle") state, otherwise it keeps low.
-2) n.valid_o is asserted on the final ("Done") state, otherwise it keeps low.
-3) The FSM stays in the Idle state while p.valid_i is low, otherwise
+1) p.o_ready is asserted on the initial ("Idle") state, otherwise it keeps low.
+2) n.o_valid is asserted on the final ("Done") state, otherwise it keeps low.
+3) The FSM stays in the Idle state while p.i_valid is low, otherwise
it accepts the input data and moves on.
-4) The FSM stays in the Done state while n.ready_i is low, otherwise
+4) The FSM stays in the Done state while n.i_ready is low, otherwise
it releases the output data and goes back to the Idle state.
"""
from nmigen import Elaboratable, Signal, Module, Cat
from nmigen.cli import rtlil
from math import log2
-import os
from nmutil.iocontrol import PrevControl, NextControl
from soc.fu.base_input_record import CompOpSubsetBase
-from soc.decoder.power_enums import (MicrOp, Function)
from nmutil.gtkw import write_gtkw
-from nmutil.sim_tmp_alternative import (Simulator, is_engine_pysim,
- nmigen_sim_top_module)
+from nmutil.sim_tmp_alternative import (Simulator, is_engine_pysim)
class CompFSMOpSubset(CompOpSubsetBase):
super().__init__(layout, name=name)
-
class Dummy:
pass
"""Simple sequential shifter
Prev port data:
- * p.data_i.data: value to be shifted
- * p.data_i.shift: shift amount
+ * p.i_data.data: value to be shifted
+ * p.i_data.shift: shift amount
* When zero, no shift occurs.
* On POWER, range is 0 to 63 for 32-bit,
* and 0 to 127 for 64-bit.
* op.sdir: shift direction (0 = left, 1 = right)
Next port data:
- * n.data_o.data: shifted value
+ * n.o_data.data: shifted value
"""
class PrevData:
def __init__(self, width):
- self.data = Signal(width, name="p_data_i")
+ self.data = Signal(width, name="p_i_data")
self.shift = Signal(width, name="p_shift_i")
- self.ctx = Dummy() # comply with CompALU API
+ self.ctx = Dummy() # comply with CompALU API
def _get_data(self):
return [self.data, self.shift]
class NextData:
def __init__(self, width):
- self.data = Signal(width, name="n_data_o")
+ self.data = Signal(width, name="n_o_data")
def _get_data(self):
return [self.data]
self.width = width
self.p = PrevControl()
self.n = NextControl()
- self.p.data_i = Shifter.PrevData(width)
- self.n.data_o = Shifter.NextData(width)
+ self.p.i_data = Shifter.PrevData(width)
+ self.n.o_data = Shifter.NextData(width)
# more pieces to make this example class comply with the CompALU API
self.op = CompFSMOpSubset(name="op")
- self.p.data_i.ctx.op = self.op
- self.i = self.p.data_i._get_data()
- self.out = self.n.data_o._get_data()
+ self.p.i_data.ctx.op = self.op
+ self.i = self.p.i_data._get_data()
+ self.out = self.n.o_data._get_data()
def elaborate(self, platform):
m = Module()
# build the data flow
m.d.comb += [
# connect input and output
- shift_in.eq(self.p.data_i.data),
- self.n.data_o.data.eq(shift_reg),
+ shift_in.eq(self.p.i_data.data),
+ self.n.o_data.data.eq(shift_reg),
# generate shifted views of the register
shift_left_by_1.eq(Cat(0, shift_reg[:-1])),
shift_right_by_1.eq(Cat(shift_reg[1:], 0)),
with m.FSM():
with m.State("IDLE"):
m.d.comb += [
- # keep p.ready_o active on IDLE
- self.p.ready_o.eq(1),
+ # keep p.o_ready active on IDLE
+ self.p.o_ready.eq(1),
# keep loading the shift register and shift count
load.eq(1),
- next_count.eq(self.p.data_i.shift),
+ next_count.eq(self.p.i_data.shift),
]
# capture the direction bit as well
m.d.sync += direction.eq(self.op.sdir)
- with m.If(self.p.valid_i):
+ with m.If(self.p.i_valid):
# Leave IDLE when data arrives
with m.If(next_count == 0):
# short-circuit for zero shift
# exit when shift counter goes to zero
m.next = "DONE"
with m.State("DONE"):
- # keep n.valid_o active while the data is not accepted
- m.d.comb += self.n.valid_o.eq(1)
- with m.If(self.n.ready_i):
+ # keep n.o_valid active while the data is not accepted
+ m.d.comb += self.n.o_valid.eq(1)
+ with m.If(self.n.i_ready):
# go back to IDLE when the data is accepted
m.next = "IDLE"
def __iter__(self):
yield self.op.sdir
- yield self.p.data_i.data
- yield self.p.data_i.shift
- yield self.p.valid_i
- yield self.p.ready_o
- yield self.n.ready_i
- yield self.n.valid_o
- yield self.n.data_o.data
+ yield self.p.i_data.data
+ yield self.p.i_data.shift
+ yield self.p.i_valid
+ yield self.p.o_ready
+ yield self.n.i_ready
+ yield self.n.o_valid
+ yield self.n.o_data.data
def ports(self):
return list(self)
{'comment': 'Shifter Demonstration'},
('prev port', [
('op__sdir', 'in'),
- ('p_data_i[7:0]', 'in'),
+ ('p_i_data[7:0]', 'in'),
('p_shift_i[7:0]', 'in'),
- ('p_valid_i', 'in'),
- ('p_ready_o' if is_engine_pysim() else 'p_p_ready_o', 'out'),
- ]),
+ ({'submodule': 'p'}, [
+ ('p_i_valid', 'in'),
+ ('p_o_ready', 'out')])]),
('internal', [
'fsm_state' if is_engine_pysim() else 'fsm_state[1:0]',
'count[3:0]',
- 'shift_reg[7:0]',
- ]),
+ 'shift_reg[7:0]']),
('next port', [
- ('n_data_o[7:0]', 'out'),
- ('n_valid_o' if is_engine_pysim() else 'n_n_valid_o', 'out'),
- ('n_ready_i', 'in'),
- ]),
- ]
+ ('n_o_data[7:0]', 'out'),
+ ({'submodule': 'n'}, [
+ ('n_o_valid', 'out'),
+ ('n_i_ready', 'in')])])]
- module = nmigen_sim_top_module + "shf"
write_gtkw("test_shifter.gtkw", "test_shifter.vcd",
gtkwave_desc, gtkwave_style,
- module=module, loc=__file__, base='dec')
+ module='top.shf', loc=__file__, base='dec')
sim = Simulator(m)
sim.add_clock(1e-6)
def send(data, shift, direction):
- # present input data and assert valid_i
- yield dut.p.data_i.data.eq(data)
- yield dut.p.data_i.shift.eq(shift)
+ # present input data and assert i_valid
+ yield dut.p.i_data.data.eq(data)
+ yield dut.p.i_data.shift.eq(shift)
yield dut.op.sdir.eq(direction)
- yield dut.p.valid_i.eq(1)
+ yield dut.p.i_valid.eq(1)
yield
- # wait for p.ready_o to be asserted
- while not (yield dut.p.ready_o):
+ # wait for p.o_ready to be asserted
+ while not (yield dut.p.o_ready):
yield
- # show current operation operation
- # force dump of the above message by toggling the
- # underlying signal
- # clear input data and negate p.valid_i
- yield dut.p.valid_i.eq(0)
- yield dut.p.data_i.data.eq(0)
- yield dut.p.data_i.shift.eq(0)
+ # clear input data and negate p.i_valid
+ yield dut.p.i_valid.eq(0)
+ yield dut.p.i_data.data.eq(0)
+ yield dut.p.i_data.shift.eq(0)
yield dut.op.sdir.eq(0)
def receive(expected):
# signal readiness to receive data
- yield dut.n.ready_i.eq(1)
+ yield dut.n.i_ready.eq(1)
yield
- # wait for n.valid_o to be asserted
- while not (yield dut.n.valid_o):
+ # wait for n.o_valid to be asserted
+ while not (yield dut.n.o_valid):
yield
# read result
- result = yield dut.n.data_o.data
- # negate n.ready_i
- yield dut.n.ready_i.eq(0)
+ result = yield dut.n.o_data.data
+ # negate n.i_ready
+ yield dut.n.i_ready.eq(0)
# check result
assert result == expected
- # finish displaying the current operation
def producer():
# 13 >> 2
# 3 << 4
yield from send(3, 4, 0)
# 21 << 0
- # use a debug signal to mark an interesting operation
- # in this case, it is a shift by zero
yield from send(21, 0, 0)
def consumer():
# 3 << 4 = 48
yield from receive(48)
# 21 << 0 = 21
- # you can look for the rising edge of this signal to quickly
- # locate this point in the traces
yield from receive(21)
sim.add_sync_process(producer)