-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathshifter.py
More file actions
95 lines (76 loc) · 3.13 KB
/
Copy pathshifter.py
File metadata and controls
95 lines (76 loc) · 3.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
"""Barrel shifter using log2 stages of muxes.
Based on FloPoCo's Shifters.cpp approach: each stage k conditionally shifts
by 2^k based on bit k of the shift amount, producing an efficient O(n log n)
barrel shifter.
"""
from amaranth import *
from math import ceil, log2
__all__ = ["Shifter"]
class Shifter(Elaboratable):
"""Barrel shifter with configurable direction and arithmetic mode.
Parameters
----------
width : int
Bit width of the input and output data.
shift_width : int
Bit width of the shift amount signal.
direction : str
Shift direction: ``"left"`` or ``"right"``.
arithmetic : bool
If True and direction is ``"right"``, performs sign-extension
(arithmetic right shift). Ignored for left shifts.
Attributes
----------
i : Signal(width), in
Data input.
shift : Signal(shift_width), in
Shift amount.
o : Signal(width), out
Shifted output.
"""
def __init__(self, width, shift_width, direction="left", arithmetic=False):
if direction not in ("left", "right"):
raise ValueError(f"direction must be 'left' or 'right', got {direction!r}")
self.width = width
self.shift_width = shift_width
self.direction = direction
self.arithmetic = arithmetic
self.i = Signal(width)
self.shift = Signal(shift_width)
self.o = Signal(width)
def elaborate(self, platform):
m = Module()
width = self.width
n_stages = self.shift_width
# Chain of intermediate values through each stage
stage = self.i
for k in range(n_stages):
shift_amount = 1 << k
next_stage = Signal(width, name=f"stage_{k}")
if self.direction == "left":
# Left shift: fill with zeros from the right
shifted = Signal(width, name=f"shifted_{k}")
m.d.comb += shifted.eq(stage << shift_amount)
m.d.comb += next_stage.eq(Mux(self.shift[k], shifted, stage))
else:
# Right shift: arithmetic (sign-extend) or logical (zero-fill)
shifted = Signal(width, name=f"shifted_{k}")
if self.arithmetic:
# Arithmetic: replicate the sign bit into vacated positions
sign_bit = stage[-1]
if shift_amount < width:
fill = Signal(shift_amount, name=f"fill_{k}")
m.d.comb += fill.eq(Mux(sign_bit, (1 << shift_amount) - 1, 0))
m.d.comb += shifted.eq(
Cat(stage[shift_amount:], fill)
)
else:
# Shift amount >= width: fill entirely with sign bit
m.d.comb += shifted.eq(Mux(sign_bit, (1 << width) - 1, 0))
else:
# Logical: zero-fill
m.d.comb += shifted.eq(stage >> shift_amount)
m.d.comb += next_stage.eq(Mux(self.shift[k], shifted, stage))
stage = next_stage
m.d.comb += self.o.eq(stage)
return m