A Barrel shifter performs the operation v << s
(or v >> s
with flipped bits) where s
is not constant.
An elegant implementation is the approach via cascaded multiplexer elements.
This cshift
primitive is later instanced procedurally, iterating through the variable i
from cyhdl import *
@block
def cshift(q : Signal.Output, a : Signal, b : Signal, sbit : Signal,
msb : Signal,
# These can take a signal or a fixed bool:
asr, rotate,
WRAP : bool):
carry = Signal(bool()) # Carry bit
u, v = [ Signal(bool()) for i in range(2) ]
@always_comb
def assign_carry():
if asr: # arithmetic shift right
carry.next = sbit & msb
else:
carry.next = 0
@always_comb
def assign():
u.next = a & ~sbit
if rotate == False:
if WRAP:
v.next = carry
else:
v.next = b & sbit
else:
v.next = b & sbit
@always_comb
def assign_q():
q.next = u | v
return instances()
@block
def shifter_stage(
shifter,
w_in : Signal,
w_out : Signal.Output,
msb : Signal,
nmux : int, sbit : Signal, DATA_WIDTH : int, W_POWER : int, asr : bool, rotate : bool
):
inst = []
# Create signal array
w = [ Signal(bool()) for i in range(DATA_WIDTH) ]
wo = concat(*reversed(w))
wi = [ w_in[i] for i in range(DATA_WIDTH) ]
MUX_W = DATA_WIDTH // nmux
for imux in range(nmux):
tmp = imux * MUX_W
# print(imux)
for i in range(tmp, tmp + MUX_W):
j = i + MUX_W//2
m = j % DATA_WIDTH
inst.append(shifter(w[m], wi[m], wi[i], sbit, msb, asr, rotate,
j >= DATA_WIDTH ))
@always_comb
def assign():
w_out.next = wo
return instances()
@block
def barrel_shifter(shifter : PassThrough(block), clk : ClkSignal, ce : Signal, val : Signal, s : Signal, result : Signal.Output, \
rotate = False, W_POWER = 5 ):
DATA_WIDTH = 2 ** W_POWER
print("DATA WIDTH", DATA_WIDTH, "ROTATE", rotate)
worker = [ val ]
worker = worker + [ Signal(intbv()[DATA_WIDTH:]) for i in range(W_POWER) ]
msb = val[DATA_WIDTH-1]
sbit = [ s[i] for i in range(len(s))]
shifter_stages = []
for stage in range(W_POWER):
K = W_POWER - stage - 1
print("Stage %d" % stage)
shifter_stages.append( \
shifter_stage(shifter, worker[stage], worker[stage + 1], msb, 2 ** stage, sbit[K], \
DATA_WIDTH, W_POWER, False, rotate) \
)
@always(clk.posedge)
def assign():
if ce == True:
result.next = worker[W_POWER]
return instances()
The translation into the target RTL or HDL takes place via a test bench factory function. This allows us to switch between different simulator back end methods. Here, we can choose between GHDL
and CXXRTL
.
from cyrite.simulation import *
from myirl.targets import pyosys
class example_design(cyrite_factory.Module):
def __init__(self, name, simclass, *args, **kwargs):
super().__init__(name, simclass, *args, **kwargs)
self.W_POWER = 4
self.debug = False
self.verbose = False
# Top level signal set
self.clk = self.ClkSignal(name="clk")
self.ce = self.Signal(bool())
self.val, self.result = [ self.Signal(intbv(0xaa00)[2 ** self.W_POWER:]) for i in range(2) ]
self.result.rename("result")
self.s = self.Signal(intbv()[self.W_POWER:])
def build(self):
return barrel_shifter(cshift,
clk = self.clk,
ce = self.ce,
val = self.val,
s = self.s,
result = self.result,
rotate = False,
W_POWER = self.W_POWER
)
def emit_rtlil(self, fileprefix):
tgt = pyosys.RTLIL("barrel_shifter")
inst = self.build()
d = inst.elab(tgt)
d[0].write_rtlil(fileprefix)
@cyrite_factory.testbench('ns')
def tb_rtl(self):
inst = self.build()
clk = self.clk
ce = self.ce
val = self.val
result = self.result
s = self.s
@self.always(delay(2))
def clkgen():
clk.next = ~clk
TEST_VALUES = [
(0xdead, 8, 0xad00),
(0x8f01, 15, 0x8000),
]
@self.sequence
def stim():
for item in TEST_VALUES:
ce.next = False
s.next = item[1]
val.next = item[0]
yield(clk.posedge)
ce.next = True
yield(clk.posedge)
yield(clk.posedge)
print(result, type(result))
assert result == item[2]
raise StopSimulation
return instances()
from yosys.simulator import CXXRTL
d = example_design("rtlil_bs", CXXRTL)
d.emit_rtlil("test")
DATA WIDTH 16 ROTATE False Stage 0 Module rtlil_bs: Existing instance cshift, rename to cshift_1 Stage 1 Module rtlil_bs: Existing instance shifter_stage, rename to shifter_stage_1 Stage 2 Module rtlil_bs: Existing instance shifter_stage, rename to shifter_stage_2 Stage 3 Module rtlil_bs: Existing instance shifter_stage, rename to shifter_stage_3 DEBUG components ['barrel_shifter_obj_wrapped_wrapperu_1u_1u_16u_4u_16_0_4', 'shifter_stage_obj_wrapped_wrapperu_16u_16c_1_1c_1_16_4_0_0', 'cshiftu_1c_1c_1u_1u_1_0_0_0', 'cshiftu_1c_1c_1u_1u_1_0_0_1', 'shifter_stage_obj_wrapped_wrapperu_16u_16c_1_2c_1_16_4_0_0', 'shifter_stage_obj_wrapped_wrapperu_16u_16c_1_4c_1_16_4_0_0', 'shifter_stage_obj_wrapped_wrapperu_16u_16c_1_8c_1_16_4_0_0'] (example_design 'rtlil_bs') DEBUG Elaborating component (example_design 'rtlil_bs') shifter_stage_obj_wrapped_wrapperu_16u_16c_1_8c_1_16_4_0_0 Adding module with name `shifter_stage_3` DEBUG Elaborating component (example_design 'rtlil_bs') shifter_stage_obj_wrapped_wrapperu_16u_16c_1_4c_1_16_4_0_0 Adding module with name `shifter_stage_2` DEBUG Elaborating component (example_design 'rtlil_bs') shifter_stage_obj_wrapped_wrapperu_16u_16c_1_2c_1_16_4_0_0 Adding module with name `shifter_stage_1` DEBUG Elaborating component (example_design 'rtlil_bs') cshiftu_1c_1c_1u_1u_1_0_0_1 Adding module with name `cshift_1` DEBUG Elaborating component (example_design 'rtlil_bs') cshiftu_1c_1c_1u_1u_1_0_0_0 Adding module with name `cshift` DEBUG Elaborating component (example_design 'rtlil_bs') shifter_stage_obj_wrapped_wrapperu_16u_16c_1_1c_1_16_4_0_0 Adding module with name `shifter_stage` Adding module with name `barrel_shifter` FINALIZE implementation `barrel_shifter` of `barrel_shifter`
from myirl.test.ghdl import GHDL
# XXX Note: we must carry out the simulation backend sequence in this order. Otherwise,
# a yet undetermined sticky effect will report a combinatorial loop error.
for sim in CXXRTL, GHDL:
d = example_design("testbench", sim)
tb = d.tb_rtl()
print(76 * '=')
tb.run(60)
Module testbench: Existing instance barrel_shifter, rename to barrel_shifter_1 DATA WIDTH 16 ROTATE False Stage 0 Module testbench: Existing instance shifter_stage, rename to shifter_stage_4 Module testbench: Existing instance cshift, rename to cshift_2 Module testbench: Existing instance cshift, rename to cshift_3 Stage 1 Module testbench: Existing instance shifter_stage, rename to shifter_stage_5 Stage 2 Module testbench: Existing instance shifter_stage, rename to shifter_stage_6 Stage 3 Module testbench: Existing instance shifter_stage, rename to shifter_stage_7 DEBUG LIB ALL ELEM (example_design 'testbench') ============================================================================ DEBUG MAIN ELAB [Instance barrel_shifter_1 I/F: [// ID: barrel_shifter_0 ]] DEBUG components ['barrel_shifter_obj_wrapped_wrapperu_1u_1u_16u_4u_16_0_4', 'shifter_stage_obj_wrapped_wrapperu_16u_16c_1_1c_1_16_4_0_0', 'cshiftu_1c_1c_1u_1u_1_0_0_0', 'cshiftu_1c_1c_1u_1u_1_0_0_1', 'shifter_stage_obj_wrapped_wrapperu_16u_16c_1_2c_1_16_4_0_0', 'shifter_stage_obj_wrapped_wrapperu_16u_16c_1_4c_1_16_4_0_0', 'shifter_stage_obj_wrapped_wrapperu_16u_16c_1_8c_1_16_4_0_0'] (example_design 'testbench') DEBUG Elaborating component (example_design 'testbench') shifter_stage_obj_wrapped_wrapperu_16u_16c_1_8c_1_16_4_0_0 Adding module with name `shifter_stage_7` DEBUG Elaborating component (example_design 'testbench') shifter_stage_obj_wrapped_wrapperu_16u_16c_1_4c_1_16_4_0_0 Adding module with name `shifter_stage_6` DEBUG Elaborating component (example_design 'testbench') shifter_stage_obj_wrapped_wrapperu_16u_16c_1_2c_1_16_4_0_0 Adding module with name `shifter_stage_5` DEBUG Elaborating component (example_design 'testbench') cshiftu_1c_1c_1u_1u_1_0_0_1 Adding module with name `cshift_3` DEBUG Elaborating component (example_design 'testbench') cshiftu_1c_1c_1u_1u_1_0_0_0 Adding module with name `cshift_2` DEBUG Elaborating component (example_design 'testbench') shifter_stage_obj_wrapped_wrapperu_16u_16c_1_1c_1_16_4_0_0 Adding module with name `shifter_stage_4` Adding module with name `barrel_shifter_1` FINALIZE implementation `barrel_shifter_1` of `barrel_shifter` Compiling /tmp/myirl_testbench_zw0rwdo5/barrel_shifter.pyx because it changed. [1/1] Cythonizing /tmp/myirl_testbench_zw0rwdo5/barrel_shifter.pyx running build_ext building 'runtime.barrel_shifter' extension creating build/temp.linux-x86_64-3.10/tmp/myirl_testbench_zw0rwdo5 gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -DCOSIM_NAMESPACE=barrel_shifter -Iruntime -I/tmp/myirl_testbench_zw0rwdo5/ -I/usr/share/yosys/include/backends/cxxrtl/runtime -I/usr/local/include/python3.10 -c /tmp/myirl_testbench_zw0rwdo5/barrel_shifter.cpp -o build/temp.linux-x86_64-3.10/tmp/myirl_testbench_zw0rwdo5/barrel_shifter.o gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -DCOSIM_NAMESPACE=barrel_shifter -Iruntime -I/tmp/myirl_testbench_zw0rwdo5/ -I/usr/share/yosys/include/backends/cxxrtl/runtime -I/usr/local/include/python3.10 -c /tmp/myirl_testbench_zw0rwdo5/barrel_shifter_rtl.cpp -o build/temp.linux-x86_64-3.10/tmp/myirl_testbench_zw0rwdo5/barrel_shifter_rtl.o g++ -pthread -shared -Wl,--strip-all build/temp.linux-x86_64-3.10/tmp/myirl_testbench_zw0rwdo5/barrel_shifter.o build/temp.linux-x86_64-3.10/tmp/myirl_testbench_zw0rwdo5/barrel_shifter_rtl.o -L/usr/local/lib -o build/lib.linux-x86_64-3.10/runtime/barrel_shifter.cpython-310-x86_64-linux-gnu.so copying build/lib.linux-x86_64-3.10/runtime/barrel_shifter.cpython-310-x86_64-linux-gnu.so -> runtime Open for writing: tb_rtl.vcd CXXRTL context: SKIP INTERFACE ITEM `shifter` CXXRTL context: SKIP INTERFACE ITEM `rotate` CXXRTL context: SKIP INTERFACE ITEM `W_POWER` <result> : 0xad00 <class 'cyrite.simulation.signals.Signal'> <result> : 0x8000 <class 'cyrite.simulation.signals.Signal'> Declare obj 'tb_rtl' in context '(example_design 'testbench')'(<class '__main__.example_design'>) Module testbench: Existing instance barrel_shifter, rename to barrel_shifter_2 DATA WIDTH 16 ROTATE False Stage 0 Module testbench: Existing instance shifter_stage, rename to shifter_stage_8 Module testbench: Existing instance cshift, rename to cshift_4 Module testbench: Existing instance cshift, rename to cshift_5 Stage 1 Module testbench: Existing instance shifter_stage, rename to shifter_stage_9 Stage 2 Module testbench: Existing instance shifter_stage, rename to shifter_stage_10 Stage 3 Module testbench: Existing instance shifter_stage, rename to shifter_stage_11 ============================================================================ Writing 'shifter_stage_11' to file /tmp/myirl_testbench_hq5dwknj/shifter_stage_11.vhdl Writing 'shifter_stage_10' to file /tmp/myirl_testbench_hq5dwknj/shifter_stage_10.vhdl Writing 'shifter_stage_9' to file /tmp/myirl_testbench_hq5dwknj/shifter_stage_9.vhdl Writing 'cshift_5' to file /tmp/myirl_testbench_hq5dwknj/cshift_5.vhdl Writing 'cshift_4' to file /tmp/myirl_testbench_hq5dwknj/cshift_4.vhdl Writing 'shifter_stage_8' to file /tmp/myirl_testbench_hq5dwknj/shifter_stage_8.vhdl Writing 'barrel_shifter_2' to file /tmp/myirl_testbench_hq5dwknj/barrel_shifter_2.vhdl Writing 'tb_rtl' to file /tmp/myirl_testbench_hq5dwknj/tb_rtl.vhdl Creating library file module_defs.vhdl
Using '/tmp/myirl_testbench_zw0rwdo5/' for output STOP SIMULATION @22
A bit more optimization can be done by moving the code into a cythonizeable library. See Cythonized barrel shifter
This primitive approach however is not very compact and this design should not be mass-instanced using CXXRTL due to issues with combinatorial logic.
See Compact barrel shifter implementation for an improved version.