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.

  1. import sys
  2. sys.dont_write_bytecode = True
  3. from amaranth import *
  4. from amaranth.lib import wiring
  5. from amaranth.lib.wiring import In, Out
  6. from amaranth.lib.enum import Enum
  7. from amaranth.back import verilog
  8. class ALUOp(Enum):
  9. ADD = 0
  10. SUB = 1
  11. SLL = 2
  12. SRA = 3
  13. SRL = 4
  14. AND = 5
  15. OR = 6
  16. XOR = 7
  17. SLT = 8
  18. SLTU = 9
  19. class ALU(wiring.Component):
  20. def __init__(self):
  21. print("### ALU constructor", file=sys.stderr)
  22. super().__init__({
  23. "I_en": In(1),
  24. "I_aluop": In(ALUOp),
  25. "I_op1": In(32),
  26. "I_op2": In(32),
  27. "O_ack": Out(1),
  28. "O_data": Out(32),
  29. "O_eq": Out(1),
  30. "O_neq": Out(1),
  31. "O_lt": Out(1),
  32. "O_ge": Out(1),
  33. "O_ltu": Out(1),
  34. "O_geu": Out(1)
  35. })
  36. def elaborate(self, platform) -> Module:
  37. m = Module()
  38. comb = m.d.comb
  39. sync = m.d.sync
  40. # The 32-bit computation result
  41. result = Signal(32)
  42. ### Shifter signals ###
  43. shift = Signal(1) # shift operation requested?
  44. shift_right = Signal(1) # is a right shift (arithmetic or logical) requested?
  45. shift_signed = Signal(1) # is this an arithmetic shift?
  46. shift_sign = Signal(1) # sign bit to be shifted in on right shifts
  47. shift_finished = Signal(1) # is the multi-cycle shifter finished?
  48. shift_counter = Signal(5) # how many bits to shift
  49. with m.Switch(self.I_aluop):
  50. with m.Case(ALUOp.SLL):
  51. comb += [shift.eq(1), shift_right.eq(0), shift_signed.eq(0)]
  52. with m.Case(ALUOp.SRA):
  53. comb += [shift.eq(1), shift_right.eq(1), shift_signed.eq(1)]
  54. with m.Case(ALUOp.SRL):
  55. comb += [shift.eq(1), shift_right.eq(1), shift_signed.eq(0)]
  56. with m.Default():
  57. comb += [shift.eq(0), shift_right.eq(0), shift_signed.eq(0)]
  58. comb += shift_sign.eq(Mux(shift_signed, self.I_op1[31], 0))
  59. sync += shift_finished.eq(0)
  60. ### Comparison flags stuff ###
  61. # Using a 33-bit subtraction result and a 32-bit xor to compute
  62. # less-than and less-than-unsigned results.
  63. # Given we need sub and xor anyways, this saves logic.
  64. eq = Signal(1)
  65. lt = Signal(1)
  66. ltu = Signal(1)
  67. sub = Signal(33) # 33 bits, additional bit for underflow detection
  68. xor = Signal(32) # 32 bits
  69. comb += {
  70. sub.eq(self.I_op1 - self.I_op2), # Subtraction with 33-bit result
  71. xor.eq(self.I_op1 ^ self.I_op2), # 32-bit xor
  72. eq.eq(self.I_op1 == self.I_op2),
  73. lt.eq(sub[32] ^ xor[31]), # signed comparison: xor underflow bit with xored sign bit
  74. ltu.eq(sub[32]) # unsigned comparison: simply look at underflow bit
  75. }
  76. with m.If(self.I_en):
  77. with m.If(~shift): # Single-cycle OPs
  78. with m.Switch(self.I_aluop):
  79. with m.Case(ALUOp.ADD):
  80. sync += result.eq(self.I_op1 + self.I_op2)
  81. with m.Case(ALUOp.SUB):
  82. sync += result.eq(sub[0:32])
  83. with m.Case(ALUOp.AND):
  84. sync += result.eq(self.I_op1 & self.I_op2)
  85. with m.Case(ALUOp.OR):
  86. sync += result.eq(self.I_op1 | self.I_op2)
  87. with m.Case(ALUOp.XOR):
  88. sync += result.eq(xor)
  89. with m.Case(ALUOp.SLT):
  90. sync += result.eq(Cat(lt, 0))
  91. with m.Case(ALUOp.SLTU):
  92. sync += result.eq(Cat(ltu, 0))
  93. with m.Else(): # Multi-cycle shifts
  94. with m.FSM():
  95. with m.State("shift_idle"):
  96. sync += result.eq(self.I_op1)
  97. sync += shift_counter.eq(self.I_op2)
  98. m.next = "shift_work"
  99. with m.State("shift_work"):
  100. with m.If(shift_counter.bool() & shift):
  101. with m.If(shift_right):
  102. sync += result.eq(Cat(result[1:32], shift_sign))
  103. with m.Else():
  104. sync += result.eq(Cat(0, result[0:31]))
  105. sync += shift_counter.eq(shift_counter - 1)
  106. with m.Else():
  107. sync += shift_finished.eq(1)
  108. m.next = "shift_finish"
  109. with m.State("shift_finish"):
  110. # just hold output for one clock cycle longer
  111. m.next = "shift_idle"
  112. ack = (~shift) | (shift & shift_finished)
  113. # wire output signals
  114. comb += [
  115. self.O_ack.eq(ack),
  116. self.O_data.eq(result),
  117. ]
  118. # register comparison flags
  119. sync += [
  120. self.O_eq.eq(eq),
  121. self.O_neq.eq(~eq),
  122. self.O_lt.eq(lt),
  123. self.O_ge.eq(~lt),
  124. self.O_ltu.eq(ltu),
  125. self.O_geu.eq(~ltu),
  126. ]
  127. return m
  128. if __name__ == "__main__":
  129. alu = ALU()
  130. with open("alu.v", "w") as f:
  131. f.write(verilog.convert(alu))