paste.mozilla.org allows you to share code snippets and notes with others.
These pastes require a link to be viewed; they are not private.
Anyone with the link is able to see the paste and also delete it.
Please refrain from sharing personal or sensitive information on this website to avoid it being viewed by other parties.
- import sys
- sys.dont_write_bytecode = True
-
- from amaranth import *
- from amaranth.lib import wiring
- from amaranth.lib.wiring import In, Out
- from amaranth.lib.enum import Enum
- from amaranth.back import verilog
-
-
- class ALUOp(Enum):
- ADD = 0
- SUB = 1
- SLL = 2
- SRA = 3
- SRL = 4
- AND = 5
- OR = 6
- XOR = 7
- SLT = 8
- SLTU = 9
-
- class ALU(wiring.Component):
-
- def __init__(self):
- print("### ALU constructor", file=sys.stderr)
- super().__init__({
- "I_en": In(1),
- "I_aluop": In(ALUOp),
- "I_op1": In(32),
- "I_op2": In(32),
- "O_ack": Out(1),
- "O_data": Out(32),
- "O_eq": Out(1),
- "O_neq": Out(1),
- "O_lt": Out(1),
- "O_ge": Out(1),
- "O_ltu": Out(1),
- "O_geu": Out(1)
- })
-
- def elaborate(self, platform) -> Module:
- m = Module()
-
- comb = m.d.comb
- sync = m.d.sync
-
- # The 32-bit computation result
- result = Signal(32)
-
- ### Shifter signals ###
-
- shift = Signal(1) # shift operation requested?
- shift_right = Signal(1) # is a right shift (arithmetic or logical) requested?
- shift_signed = Signal(1) # is this an arithmetic shift?
- shift_sign = Signal(1) # sign bit to be shifted in on right shifts
- shift_finished = Signal(1) # is the multi-cycle shifter finished?
- shift_counter = Signal(5) # how many bits to shift
- with m.Switch(self.I_aluop):
- with m.Case(ALUOp.SLL):
- comb += [shift.eq(1), shift_right.eq(0), shift_signed.eq(0)]
- with m.Case(ALUOp.SRA):
- comb += [shift.eq(1), shift_right.eq(1), shift_signed.eq(1)]
- with m.Case(ALUOp.SRL):
- comb += [shift.eq(1), shift_right.eq(1), shift_signed.eq(0)]
- with m.Default():
- comb += [shift.eq(0), shift_right.eq(0), shift_signed.eq(0)]
- comb += shift_sign.eq(Mux(shift_signed, self.I_op1[31], 0))
- sync += shift_finished.eq(0)
-
- ### Comparison flags stuff ###
- # Using a 33-bit subtraction result and a 32-bit xor to compute
- # less-than and less-than-unsigned results.
- # Given we need sub and xor anyways, this saves logic.
-
- eq = Signal(1)
- lt = Signal(1)
- ltu = Signal(1)
- sub = Signal(33) # 33 bits, additional bit for underflow detection
- xor = Signal(32) # 32 bits
-
- comb += {
- sub.eq(self.I_op1 - self.I_op2), # Subtraction with 33-bit result
- xor.eq(self.I_op1 ^ self.I_op2), # 32-bit xor
- eq.eq(self.I_op1 == self.I_op2),
- lt.eq(sub[32] ^ xor[31]), # signed comparison: xor underflow bit with xored sign bit
- ltu.eq(sub[32]) # unsigned comparison: simply look at underflow bit
- }
-
- with m.If(self.I_en):
- with m.If(~shift): # Single-cycle OPs
- with m.Switch(self.I_aluop):
- with m.Case(ALUOp.ADD):
- sync += result.eq(self.I_op1 + self.I_op2)
- with m.Case(ALUOp.SUB):
- sync += result.eq(sub[0:32])
- with m.Case(ALUOp.AND):
- sync += result.eq(self.I_op1 & self.I_op2)
- with m.Case(ALUOp.OR):
- sync += result.eq(self.I_op1 | self.I_op2)
- with m.Case(ALUOp.XOR):
- sync += result.eq(xor)
- with m.Case(ALUOp.SLT):
- sync += result.eq(Cat(lt, 0))
- with m.Case(ALUOp.SLTU):
- sync += result.eq(Cat(ltu, 0))
-
- with m.Else(): # Multi-cycle shifts
- with m.FSM():
- with m.State("shift_idle"):
- sync += result.eq(self.I_op1)
- sync += shift_counter.eq(self.I_op2)
- m.next = "shift_work"
- with m.State("shift_work"):
- with m.If(shift_counter.bool() & shift):
- with m.If(shift_right):
- sync += result.eq(Cat(result[1:32], shift_sign))
- with m.Else():
- sync += result.eq(Cat(0, result[0:31]))
- sync += shift_counter.eq(shift_counter - 1)
- with m.Else():
- sync += shift_finished.eq(1)
- m.next = "shift_finish"
- with m.State("shift_finish"):
- # just hold output for one clock cycle longer
- m.next = "shift_idle"
-
- ack = (~shift) | (shift & shift_finished)
-
- # wire output signals
- comb += [
- self.O_ack.eq(ack),
- self.O_data.eq(result),
- ]
- # register comparison flags
- sync += [
- self.O_eq.eq(eq),
- self.O_neq.eq(~eq),
- self.O_lt.eq(lt),
- self.O_ge.eq(~lt),
- self.O_ltu.eq(ltu),
- self.O_geu.eq(~ltu),
- ]
-
- return m
-
- if __name__ == "__main__":
-
- alu = ALU()
-
- with open("alu.v", "w") as f:
- f.write(verilog.convert(alu))