summaryrefslogtreecommitdiff
path: root/hdl/core.py
diff options
context:
space:
mode:
Diffstat (limited to 'hdl/core.py')
-rw-r--r--hdl/core.py584
1 files changed, 0 insertions, 584 deletions
diff --git a/hdl/core.py b/hdl/core.py
deleted file mode 100644
index 2b7a977..0000000
--- a/hdl/core.py
+++ /dev/null
@@ -1,584 +0,0 @@
-from ast import Del
-from curses.ascii import SI
-from multiprocessing import dummy
-from tkinter import S, Y
-from amaranth import *
-from amaranth.sim import Simulator, Settle, Delay
-from enum import Enum, unique
-
-from utils import cmd
-
-
-class Reg(Elaboratable):
- def __init__(self, sim=False): # sim is only for modularity, does nothing for this
- # enable write
- self.wr_en = Signal(1)
-
- # input and output addresses
- self.rd_addr = Signal(4)
- self.rs1_addr = Signal(4)
- self.rs2_addr = Signal(4)
-
- # input and output signals
- self.rd = Signal(32)
- self.rs1 = Signal(32)
- self.rs2 = Signal(32)
-
- #interupt enable output signal
- self.int_en = Signal(1)
-
- # alu status signals
- self.alu_flgs = Signal(5)
-
- # signals stack operation
- self.stack_instr = Signal(1)
- self.stack_down_up = Signal(1)
-
- ##################################################################
-
- # this is the singal from the control unit, it not affected by interupt enable
- self.int_sig = Signal(1)
- # return from interrupt special
- self.iret = Signal(1)
- # jump signal to jump and link (swap ip and cs0)
- self.jump = Signal(1)
-
- #################################################################
- # None of the 3 signals above should ever be set at the same time
- #################################################################
-
-
- # internal signals
- self._user_mode = Signal(1)
- self._sp_inc_dec = Signal(1)
- self._wr_alu_flg = Signal(1)
-
- self.zx = Signal(32) #0
- self.ax = Signal(32) #1
- self.bx = Signal(32) #2
- self.bx = Signal(32) #3
- self.cx = Signal(32) #4
- self.dx = Signal(32) #5
- self.ex = Signal(32) #6
- self.fx = Signal(32) #7
- self.gx = Signal(32) #8
- self.hx = Signal(32) #9
- self.ip = Signal(32) #10
- self.sp = Signal(32) #11
- self.flg = Signal(32) #12
- self.cs0 = Signal(32) #13
- self.cs1 = Signal(32) #14
- self.cs2 = Signal(32) #15
- self.pda = Signal(32) #16
-
- # for sake of modularity, make bit locations easily configurable
- setattr(self.flg, 'c', self.flg[0])
- setattr(self.flg, 'ov', self.flg[1])
- setattr(self.flg, 'z', self.flg[2])
- setattr(self.flg, 'n', self.flg[3])
- setattr(self.flg, 'od', self.flg[4])
- setattr(self.flg, 'int', self.flg[16])
- setattr(self.flg, 'user_mode', self.flg[17])
- setattr(self.flg, 'page_en', self.flg[18])
-
-
- reg_list = [self.zx, self.ax, self.bx, self.cx, self.dx, self.ex, self.fx, self.gx, self.hx, self.ip, self.sp, self.flg, self.cs0, self.cs1, self.cs2, self.pda]
- for idx, reg in enumerate(reg_list):
- setattr(reg, 'idx', idx) # set idx attribute to each register
-
- self.reg_arr = Array(reg_list)
- self.ports = [self.wr_en, self.alu_flgs, self.jump, self.int_sig, self.int_en, self.iret, self.stack_instr, self.stack_down_up, self.rd_addr, self.rs1_addr, self.rs2_addr, self.rd, self.rs1, self.rs2, self.ip]
-
- def elaborate(self, platform=None):
- m = Module()
-
- # user mode override
- m.d.comb += self.int_en.eq(self.flg.int)
- m.d.comb += self._user_mode.eq(self.flg.user_mode & ~self.int_sig)
-
- # toggle ip and cs0
- with m.If(self.jump | self.int_sig | self.iret):
- m.d.sync += self.cs0.eq(self.ip)
- m.d.sync += self.ip.eq(self.cs0)
- with m.Else():
- m.d.sync += self.ip.eq(self.ip + 1) # increment ip only on normal operation
-
- with m.If(self.int_sig):
- m.d.sync += self.cs1.eq(self.sp)
- m.d.sync += self.cs2.eq(self.flg)
- m.d.sync += self.flg.user_mode.eq(0) # set to system mode or iret cannot be used
- m.d.sync += self.flg.int.eq(0) # clear int flag, essential because another interrupt can be triggered without this
- with m.Elif(self.iret):
- m.d.sync += self.sp.eq(self.cs1)
- m.d.sync += self.flg.eq(self.cs2)
-
- m.d.comb += self._sp_inc_dec.eq(0)
- m.d.comb += self._wr_alu_flg.eq(1)
-
- # writeback setup
- with m.If(self.wr_en):
- with m.Switch(self.rd_addr):
- with m.Case(self.zx.idx): # do not write to zero register
- pass
-
- with m.Case(self.ip.idx): #do not directly write to ip register
- pass
-
- with m.Case(self.flg.idx):
- # mask top half of register to prevent writing to flags in user mode
- with m.If(~self._user_mode):
- m.d.sync += self.flg.eq(self.rd) # system mode, full control
- with m.Else():
- m.d.sync += self.flg.eq(Cat(self.rd[:16], self.flg[16:])) # usermode can only effect lower 16 bits
-
- # don't update flags from alu
- m.d.comb += self._wr_alu_flg.eq(0)
-
- with m.Case(self.sp.idx):
- m.d.comb += self._sp_inc_dec.eq(1)
- m.d.sync += self.sp.eq(self.rd)
-
- with m.Case():
- m.d.sync += self.reg_arr[self.rd_addr].eq(self.rd)
-
- with m.If(self._wr_alu_flg):
- m.d.sync += self.flg.eq(Cat(self.alu_flgs, self.flg[len(self.alu_flgs):])) # update flags if register is not being written to
-
- with m.If(~self._sp_inc_dec & self.stack_instr):
- with m.If(self.stack_down_up):
- m.d.sync += self.sp.eq(self.sp - 1)
- with m.Else():
- m.d.sync += self.sp.eq(self.sp + 1)
-
-
- ### Combination signal outputs ###
- with m.Switch(self.rs1_addr):
- with m.Case(self.flg.idx):
- m.d.comb += self.rs1.eq(self.flg & Cat(Const(0xFFFF, 16), Repl(~self._user_mode, 16)))
- with m.Case():
- m.d.comb += self.rs1.eq(self.reg_arr[self.rs1_addr])
-
- with m.Switch(self.rs2_addr):
- with m.Case(self.flg.idx):
- m.d.comb += self.rs2.eq(self.flg & Cat(Const(0xFFFF, 16), Repl(~self._user_mode, 16)))
- with m.Case():
- m.d.comb += self.rs2.eq(self.reg_arr[self.rs2_addr])
-
- return m
-
-def test_reg(filename="reg.vcd"):
- dut = Reg(sim=True)
-
- def init():
- for i in range(16):
- yield dut.reg_arr[i].eq(i)
- yield Settle()
-
- def step():
- yield Settle() # settle comb logic before clock
- yield # clock edge
- yield Settle() # settle comb logic after clock
- yield Delay(5e-7) # used for debugging, change values on neg edge of clock
-
- def proc1():
- yield from init()
-
- # test combinational output
- for i in range(16):
- yield dut.rs1_addr.eq(i)
- yield Settle()
- assert (yield dut.rs1) == i, f'ERROR reading {dut.reg_arr[i].name} != {i}'
- for i in range(16):
- yield dut.rs2_addr.eq(i)
- yield Settle()
- assert (yield dut.rs2) == i, f'ERROR reading {dut.reg_arr[i].name} != {i}'
-
- # test writeback with writeback disabled
- yield from init()
- for i in range(16):
- yield dut.rd_addr.eq(i)
- yield dut.rd.eq(i + 1)
- yield from step()
- if (i != dut.ip.idx) and (i != dut.flg.idx): # flag gets update by the alu
- assert (yield dut.reg_arr[i]) == i, f'ERROR writing to {dut.reg_arr[i].name} != {i}'
-
- # test writeback with writeback enabled
- for i in range(16):
- yield from init()
- yield dut.wr_en.eq(1)
- yield dut.rd_addr.eq(i)
- yield dut.rd.eq(i - 1)
- yield from step()
- if (i != dut.zx.idx) and (i != dut.ip.idx):
- assert (yield dut.reg_arr[i]) == i-1, f'ERROR writing to {dut.reg_arr[i].name} != {i-1}'
- elif i == dut.zx.idx:
- assert (yield dut.zx) == 0, f'ERROR {dut.zx.name} != 0'
- elif i == dut.ip.idx:
- # ip should be incremented and not written to
- assert (yield dut.reg_arr[i]) == i+1, f'ERROR {dut.ip.name} != {i + 1} should not be able to be directly written to'
-
- yield dut.wr_en.eq(0)
-
- # test flag register security
- yield dut.flg.eq(0)
- yield dut.flg.user_mode.eq(1)
- yield dut.flg[15].eq(1)
- yield dut.flg[31].eq(1)
- yield dut.rs1_addr.eq(dut.flg.idx)
- yield Settle()
- assert (yield dut.rs1) == 0x00008000, f'ERROR: able to read upper 16 bits of flg reg in user mode'
-
- # test flag register security
- yield dut.flg.eq(0)
- yield dut.wr_en.eq(1)
- yield dut.flg.user_mode.eq(1)
- yield dut.flg[15].eq(1)
- yield dut.flg[31].eq(1)
- yield dut.rd_addr.eq(dut.flg.idx)
- yield dut.rd.eq(0xABCD5789)
- yield from step()
- assert (yield dut.flg) == (0x80020000 | 0x5789), f'ERROR: able to write to upper 16 bits of flg reg in user mode'
-
- yield dut.flg.eq(0)
- yield dut.flg.user_mode.eq(0)
- yield dut.flg[15].eq(1)
- yield dut.flg[31].eq(1)
- yield dut.rs1_addr.eq(dut.flg.idx)
- yield Settle()
- assert (yield dut.rs1) == 0x80008000, f'ERROR: able to read all bits of flg reg in system mode'
-
- yield dut.flg.eq(0)
- yield dut.wr_en.eq(1)
- yield dut.flg.user_mode.eq(0)
- yield dut.flg[15].eq(1)
- yield dut.flg[31].eq(1)
- yield dut.rd_addr.eq(dut.flg.idx)
- yield dut.rd.eq(0xABCD5789)
- yield from step()
- assert (yield dut.flg) == (0xABCD5789), f'ERROR: unamble to write to all bits in supervisor mode'
-
- # make sure not to write alu flags when directly writing to flg register
- yield dut.flg.eq(0)
- yield dut.alu_flgs.eq(Repl(1, dut.alu_flgs.width))
- yield dut.wr_en.eq(1)
- yield dut.flg.user_mode.eq(0)
- yield dut.flg[15].eq(1)
- yield dut.flg[31].eq(1)
- yield dut.rd_addr.eq(dut.flg.idx)
- yield dut.rd.eq(0xFFFF0000)
- yield from step()
- assert (yield dut.flg) == (0xFFFF0000), f'ERROR: alu status should not be to flag'
-
- # check to make sure alu is writing values
- yield dut.flg.eq(0)
- yield dut.alu_flgs.eq(Repl(1, dut.alu_flgs.width))
- yield dut.wr_en.eq(1)
- yield dut.flg.user_mode.eq(0)
- yield dut.rd_addr.eq(dut.zx.idx) # can be anything except flg register
- yield dut.rd.eq(0xFFFF0000) # this does not matter
- yield from step()
- assert (yield dut.flg) == (yield dut.alu_flgs), f'ERROR: alu is not writing to flg register'
-
-
-
-
- sim = Simulator(dut)
- sim.add_clock(1e-6)
- sim.add_sync_process(proc1)
-
- with sim.write_vcd(filename):
- sim.run()
-
-
-
-# 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
-
-@unique
-class AluOpCodes(Enum):
- add = 0
- addc = 1
- sub = 2
- subc = 3
- bit_and = 4
- bit_or = 5
- bit_xor = 6
- bit_nor = 7
- lleft = 8
- lright = 9
- aright = 10
- set_bit = 11
- clear_bit = 12
- umult = 13
- smult = 14
- udiv = 15
- sdiv = 16
-
-class ALU(Elaboratable):
- def __init__(self, sim=False):
- self.in1 = Signal(32, reset_less=True)
- self.in2 = Signal(32, reset_less=True)
- self.c_in = Signal(1)
- self.out = Signal(32, reset_less=True)
- self.op = Signal(4, reset_less=True)
-
- self.tmp = Signal(33, reset_less=True)
-
- self.c_out = Signal(1, reset_less=True)
- self.overflow = Signal(1, reset_less=True)
- self.zero = Signal(1, reset_less=True)
- self.neg = Signal(1, reset_less=True)
- self.odd = Signal(1, reset_less=True)
-
- self.sim = sim
- self.ports = [self.in1, self.in2, self.op, self.c_in, self.out, self.c_out, self.overflow, self.zero, self.neg]
-
- 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(AluOpCodes.add.value):
- m.d.comb += self.tmp.eq(self.in1 + self.in2)
-
- with m.Case(AluOpCodes.addc.value):
- m.d.comb += self.tmp.eq(self.in1 + self.in2 + self.c_in)
-
- with m.Case(AluOpCodes.sub.value):
- m.d.comb += self.tmp.eq(self.in1 - self.in2)
-
- with m.Case(AluOpCodes.subc.value):
- m.d.comb += self.tmp.eq(self.in1 + (~self.in2 + self.c_in))
-
- with m.Case(AluOpCodes.bit_and.value):
- m.d.comb += self.tmp.eq(Cat(self.in1 & self.in2, 0))
-
- with m.Case(AluOpCodes.bit_or.value):
- m.d.comb += self.tmp.eq(Cat(self.in1 | self.in2, 0))
-
- with m.Case(AluOpCodes.bit_xor.value):
- m.d.comb += self.tmp.eq(Cat(self.in1 ^ self.in2, 0))
-
- with m.Case(AluOpCodes.bit_nor.value):
- m.d.comb += self.tmp.eq(Cat(~(self.in1 | self.in2), 0))
-
- with m.Case(AluOpCodes.lleft.value):
- m.d.comb += self.tmp.eq(Cat(self.in1, 0) << self.in2[0:5])
-
- with m.Case(AluOpCodes.lright.value):
- tmp2 = Signal(33)
- m.d.comb += tmp2.eq(Cat(0, self.in1) >> self.in2[0:5])
- m.d.comb += self.tmp.eq(Cat(tmp2[1:33], tmp2[0])) # move shifted bit to carry bit
-
- with m.Case(AluOpCodes.aright.value):
- tmp2 = Signal(33)
- m.d.comb += tmp2.eq(Cat(0, self.in1).as_signed() >> self.in2[0:5])
- m.d.comb += self.tmp.eq(Cat(tmp2[1:33], tmp2[0])) # move shifted bit to carry bit
-
- with m.Case(AluOpCodes.set_bit.value):
- m.d.comb += self.tmp.eq(Cat(self.in1 | (1 << self.in2[0:5]), 0))
-
- with m.Case(AluOpCodes.clear_bit.value):
- m.d.comb += self.tmp.eq(Cat(self.in1 & ~(1 << self.in2[0:5]), 0))
-
- with m.Case(AluOpCodes.umult.value):
- m.d.comb += self.tmp.eq(Cat(self.in1[0:16] * self.in2[0:16], 0))
-
- with m.Case(AluOpCodes.smult.value):
- m.d.comb += self.tmp.eq(Cat(self.in1[0:16].as_signed() * self.in2[0:16].as_signed(), 0))
-
-
- # bad juju,
- # TODO: come back and check this will work
- # with m.Case(AluOpCodes.udiv.value):
- # m.d.comb += self.tmp.eq(Cat(self.in1 // self.in2, 0))
-
- # with m.Case(AluOpCodes.sdiv.value):
- # m.d.comb += self.tmp.eq(self.in1.as_signed() // self.in2.as_signed()) # for some reason I have not confirmed, signed div can yield a 33 bit number, acording to amaranth
-
- with m.Case():
- m.d.comb += self.tmp.eq(0)
-
- m.d.comb += self.c_out.eq(self.tmp[32])
- m.d.comb += self.overflow.eq(self.tmp[32] ^ self.tmp[31])
- m.d.comb += self.out.eq(self.tmp[0:32])
- m.d.comb += self.neg.eq(self.out[31])
- m.d.comb += self.zero.eq(self.out == 0)
- m.d.comb += self.odd.eq(self.out.xor()) # 1 if odd number of bits, 0 if even
-
- return m
-
-def test_alu(filename="alu.vcd"):
- dut = ALU(sim=True)
-
- def proc1():
- def sub_proc(val1, val2, c_in=0):
- yield dut.in1.eq(val1)
- yield dut.in2.eq(val2)
- yield dut.c_in.eq(c_in)
- yield
- yield Settle()
-
- # test addition
- yield dut.op.eq(AluOpCodes.add.value)
- yield from sub_proc(27, 13)
- out = yield dut.out
- assert 27 + 13 == (out), f'ERROR: {out} != {27 + 13}'
-
- # test addition with carry
- yield dut.op.eq(AluOpCodes.addc.value)
- yield from sub_proc(11, 43, 1)
- out = yield dut.out.as_signed()
- assert 11 + 43 + 1 == out, f'ERROR: {out} != {11 + 43 + 1}'
-
- # test subtraction
- yield dut.op.eq(AluOpCodes.sub.value)
- yield from sub_proc(25, 13)
- out = yield dut.out
- assert 25 - 13 == out, f'ERROR: {out} != {25 - 13}'
-
- # test subtraction with carry
- yield dut.op.eq(AluOpCodes.subc.value)
- yield from sub_proc(25, -13, 0)
- out = yield dut.out.as_signed()
- assert 25 + 13 -1 +0 == out, f'ERROR: {out} != {25 + 13 -1 +0}'
-
- # test subtraction with carry
- yield dut.op.eq(AluOpCodes.subc.value)
- yield from sub_proc(25, -13, 1)
- out = yield dut.out.as_signed()
- assert 25 + 13 -1 +1 == out, f'ERROR: {out} != {25 + 13 -1 +1}'
-
- # test binary and
- yield dut.op.eq(AluOpCodes.bit_and.value)
- yield from sub_proc(0b10101011, 0b01010101)
- out = yield dut.out
- assert 0b00000001 == out, f'ERROR: {out} != {0b00000001}'
-
- # test binary or
- yield dut.op.eq(AluOpCodes.bit_or.value)
- yield from sub_proc(0b10101011, 0b01000101)
- out = yield dut.out
- assert 0b11101111 == out, f'ERROR: {out} != {0b11101111}'
-
- # test binary nor
- yield dut.op.eq(AluOpCodes.bit_nor.value)
- yield from sub_proc(0b10001011, 0b01000101)
- out = yield dut.out
- assert 0b11111111111111111111111100110000 == out, f'ERROR: {bin(out)} != {bin(0b11111111111111111111111100110000)}'
-
- # test binary xor
- yield dut.op.eq(AluOpCodes.bit_xor.value)
- 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(AluOpCodes.lleft.value)
- yield from sub_proc(0b10001011, 25) # shift left by 5
- out = yield dut.out
- assert 0b00010110000000000000000000000000 == out, f'ERROR: {bin(out)} != {bin(0b00010110000000000000000000000000)}'
- out = yield dut.c_out
- assert 1 == out, f'ERROR: {out} != {1}'
-
- # test logical shift right
- yield dut.op.eq(AluOpCodes.lright.value)
- yield from sub_proc(0b10001011, 4) # shift right by 5
- out = yield dut.out
- assert 0b1000 == out, f'ERROR: {bin(out)} != {bin(0b1000)}'
- out = yield dut.c_out
- assert 1 == out, f'ERROR: {out} != {1}'
-
- # test aligned shift right
- yield dut.op.eq(AluOpCodes.aright.value)
- yield from sub_proc(0x80001234, 4) # shift right by 4
- out = yield dut.out
- assert 0xF8000123 == out, f'ERROR: {out} != {0xF8000123}'
- out = yield dut.c_out
- assert 0 == out, f'ERROR: {out} != {0}'
-
- # test unsigned overflow
- yield dut.op.eq(AluOpCodes.add.value)
- yield from sub_proc(0xFFFFFFFF, 1) # add 1 to 0xFFFFFFFF
- out = yield dut.overflow
- assert out == 1, f'ERROR: {out} != {1}'
- out = yield dut.c_out
- assert out == 1, f'ERROR: {out} != {1}'
-
- # test unsigned underflow
- yield dut.op.eq(AluOpCodes.add.value)
- yield from sub_proc(0, -1) # subtract 1 from 0
- out = yield dut.overflow
- assert out == 1, f'ERROR: {out} != {1}'
- out = yield dut.c_out
- assert out == 0, f'ERROR: {out} != {0}'
-
- # test zero
- yield dut.op.eq(AluOpCodes.add.value)
- 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(AluOpCodes.add.value)
- yield from sub_proc(0, 1) # add 0 to 0
- out = yield dut.zero
- assert out == 0, f'ERROR: {out} != {0}'
-
- # test odd
- yield dut.op.eq(AluOpCodes.add.value)
- yield from sub_proc(0, 0xAAAAAAAA) # add 0 to 0
- out = yield dut.odd
- assert out == 0, f'ERROR: {out} != {0}'
-
- # test odd
- yield dut.op.eq(AluOpCodes.add.value)
- yield from sub_proc(0, 0xAAAAAAAB) # add 0 to 0
- out = yield dut.odd
- assert out == 1, f'ERROR: {out} != {1}'
-
-
- sim = Simulator(dut)
- sim.add_clock(1e-6)
- sim.add_sync_process(proc1)
-
- with sim.write_vcd(filename):
- sim.run()
-
-
-
-if __name__ == '__main__':
- # reg = Reg()
- # cmd(reg, test_reg)
-
- hdl = ALU()
- cmd(hdl, test_alu)