from multiprocessing import dummy from amaranth import * from amaranth.sim import Simulator, Settle, Delay from utils import cmd # class Reg(Elaboratable): # def __init__(self): # self.rd_addr = Signal(4) # self.rs1_addr = Signal(4) # self.rs2_addr = Signal(4) # self.rd = Signal(32) # self.rs1 = Signal(32) # self.rs2 = Signal(32) # self.zx = Signal(32) # self.ax = Signal(32) # self.bx = Signal(32) # self.bx = Signal(32) # self.cx = Signal(32) # self.dx = Signal(32) # self.ex = Signal(32) # self.fx = Signal(32) # self.gx = Signal(32) # self.hx = Signal(32) # self.hi = Signal(32) # self.lo = Signal(32) # self.fg = Signal(32) # self.cr = Signal(32) # self.ip = Signal(32) # self.sp = Signal(32) # self.ja = Signal(32) # self.reg_ar = Array([self.zx, self.ax, self.bx, self.cx, self.dx, self.ex, self.fx, self.gx, self.hx, self.hi, self.lo, self.fg, self.cr, self.ip, self.sp, self.ja]) # # TODO: add support for storing multiplication result # self.ports = [self.rd_addr, self.rs1_addr, self.rs2_addr, self.rd, self.rs1, self.rs2, self.ip] # def elaborate(self, platform=None): # m = Module() # with m.If(self.rd_addr != 0): # m.d.sync += self.reg_ar[self.rd_addr].eq(self.rd) # m.d.comb += self.rs1.eq(self.reg_ar[self.rs1_addr]) # m.d.comb += self.rs2.eq(self.reg_ar[self.rs2_addr]) # return m # class ASAP32Core(Elaboratable): # def __init__(self): # self.interupt_msk = Signal(32) # self.interupt_addr = Signal(32) # self.interupt_en = Signal(1) # self.interupt_sig = Signal(1) # self.jump = Signal(1) # self.instruction_addr = Signal(32) # self.ports = [] # def elaborate(self, platform=None): # m = Module() # m.submodules.reg = reg = Reg() # # interupt setup # m.d.comb += self.interupt_en.eq(reg.cr[0]) # # get instruction address, account for jumps and interupts # m.d.sync += self.instruction_addr.eq(Mux(self.interupt_en & self.interupt_sig, self.interupt_addr, Mux(self.jump, reg.ja, reg.ip))) # # update program counter # m.d.sync += reg.ip.eq(self.instruction_addr + 1) # return m class ALU(Elaboratable): def __init__(self, sim=False): self.in1 = Signal(32, reset_less=True) self.in2 = Signal(32, reset_less=True) self.out = Signal(32, reset_less=True) self.op = Signal(4, reset_less=True) self.tmp = Signal(33, reset_less=True) self.signed_op = Signal(1, reset_less=True) self.carry = Signal(1, reset_less=True) self.overflow = Signal(1, reset_less=True) self.zero = Signal(1, reset_less=True) self.sign = Signal(1, reset_less=True) self.sim = sim self.ports = [self.in1, self.in2, self.op, self.out, self.carry, self.overflow, self.zero, self.sign] def elaborate(self, platform=None): m = Module() # dummy sync for simulation only if self.sim == True: dummy = Signal() m.d.sync += dummy.eq(~dummy) with m.Switch(self.op): with m.Case(0b0000): m.d.comb += self.tmp.eq(self.in1 + self.in2) with m.Case(0b0010): m.d.comb += self.tmp.eq(self.in1.as_signed() + self.in2.as_signed()) m.d.comb += self.signed_op.eq(1) with m.Case(0b0001): m.d.comb += self.tmp.eq(self.in1 - self.in2) with m.Case(0b0011): m.d.comb += self.tmp.eq(self.in1.as_signed() - self.in2.as_signed()) m.d.comb += self.signed_op.eq(1) with m.Case(4): m.d.comb += self.tmp.eq(Cat(self.in1 & self.in2, 0)) with m.Case(5): m.d.comb += self.tmp.eq(Cat(self.in1 | self.in2, 0)) with m.Case(6): m.d.comb += self.tmp.eq(Cat(self.in1 ^ self.in2, 0)) with m.Case(7): m.d.comb += self.tmp.eq(Cat(self.in1 << self.in2[0:5], 0)) with m.Case(8): m.d.comb += self.tmp.eq(Cat(self.in1 >> self.in2[0:5], 0)) with m.Case(9): m.d.comb += self.tmp.eq(Cat(self.in1.as_signed() >> self.in2[0:5], 0)) with m.Case(): m.d.comb += self.signed_op.eq(0) m.d.comb += self.tmp.eq(0) m.d.comb += self.carry.eq(self.tmp[32]) m.d.comb += self.overflow.eq(self.tmp[32] ^ self.tmp[31]) m.d.comb += self.sign.eq(self.tmp.as_signed() < 0) m.d.comb += self.zero.eq(self.out == 0) m.d.comb += self.out.eq(self.tmp[0:32]) return m def test_alu(filename="alu.vcd"): dut = ALU(sim=True) def proc1(): def sub_proc(val1, val2): yield dut.in1.eq(val1) yield dut.in2.eq(val2) yield yield Settle() # test unsigned addition yield dut.op.eq(0b0000) yield from sub_proc(27, 13) out = yield dut.out assert 27 + 13 == (out), f'ERROR: {out} != {27 + 13}' # test signed addition yield dut.op.eq(0b0010) yield from sub_proc(-11, 43) out = yield dut.out.as_signed() assert -11 + 43 == out, f'ERROR: {out} != {-11 + 43}' # test unsigned subtraction yield dut.op.eq(0b0001) yield from sub_proc(25, 13) out = yield dut.out assert 25 - 13 == out, f'ERROR: {out} != {25 - 13}' # test signed subtraction yield dut.op.eq(0b0011) yield from sub_proc(25, -13) out = yield dut.out.as_signed() assert 25 + 13 == out, f'ERROR: {out} != {25 + 13}' # test unsigned logical and yield dut.op.eq(4) yield from sub_proc(0b10101011, 0b01010101) out = yield dut.out assert 0b00000001 == out, f'ERROR: {out} != {0b00000001}' # test unsigned logical or yield dut.op.eq(5) yield from sub_proc(0b10101011, 0b01000101) out = yield dut.out assert 0b11101111 == out, f'ERROR: {out} != {0b11101111}' # test logical xor yield dut.op.eq(6) yield from sub_proc(0b10001011, 0b01000101) out = yield dut.out assert 0b11001110 == out, f'ERROR: {out} != {0b11001110}' # test logical shift left yield dut.op.eq(7) yield from sub_proc(0b10001011, 5) # shift left by 5 out = yield dut.out assert 0b1000101100000 == out, f'ERROR: {out} != {0b1000101100000}' # test logical shift right yield dut.op.eq(8) yield from sub_proc(0b10001011, 5) # shift right by 5 out = yield dut.out assert 0b100 == out, f'ERROR: {out} != {0b100}' # test aligned shift right yield dut.op.eq(9) yield from sub_proc(0x80001234, 4) # shift right by 4 out = yield dut.out assert 0xF8000123 == out, f'ERROR: {out} != {0xF8000123}' # test unsigned overflow yield dut.op.eq(0b0000) yield from sub_proc(0xFFFFFFFF, 1) # add 1 to 0xFFFFFFFF out = yield dut.overflow assert out == 1, f'ERROR: {out} != {1}' out = yield dut.carry assert out == 1, f'ERROR: {out} != {1}' # test signed overflow yield dut.op.eq(0b0010) yield from sub_proc(0x7FFFFFFF, 1) # add 1 to 0x7FFFFFFF out = yield dut.overflow assert out == 1, f'ERROR: {out} != {1}' out = yield dut.carry assert out == 0, f'ERROR: {out} != {0}' # test unsigned underflow yield dut.op.eq(0b0001) yield from sub_proc(0, -1) # subtract 1 from 0 out = yield dut.overflow assert out == 1, f'ERROR: {out} != {1}' out = yield dut.carry assert out == 1, f'ERROR: {out} != {1}' # test signed underflow yield dut.op.eq(0b0010) yield from sub_proc(0x80000000, -1) # sub 1 from 0x80000000 (most negative number in two's complement) assert out == 1, f'ERROR: {out} != {1}' out = yield dut.carry assert out == 1, f'ERROR: {out} != {1}' # test zero yield dut.op.eq(0b0000) yield from sub_proc(0, 0) # add 0 to 0 out = yield dut.zero assert out == 1, f'ERROR: {out} != {1}' # test zero yield dut.op.eq(0b0000) yield from sub_proc(0, 1) # add 0 to 0 out = yield dut.zero assert out == 0, f'ERROR: {out} != {0}' sim = Simulator(dut) sim.add_clock(1e-6) sim.add_sync_process(proc1) with sim.write_vcd(filename): sim.run() if __name__ == '__main__': hdl = ALU() cmd(hdl, test_alu)