208 lines
6.7 KiB
Python
208 lines
6.7 KiB
Python
import cocotb
|
|
from cocotb.handle import Immediate
|
|
|
|
from cocotb.clock import Clock
|
|
from cocotb.triggers import Timer, RisingEdge
|
|
|
|
from collections import defaultdict
|
|
|
|
CLK_PERIOD = 5
|
|
|
|
memory = defaultdict(int)
|
|
|
|
def write_dword(addr: int, data: int):
|
|
memory[addr + 0] = (data >> 0) & 0xff
|
|
memory[addr + 1] = (data >> 8) & 0xff
|
|
memory[addr + 2] = (data >> 16) & 0xff
|
|
memory[addr + 3] = (data >> 24) & 0xff
|
|
|
|
def write_byte(addr: int, data: int):
|
|
memory[addr] = data & 0xff
|
|
|
|
def write_bytes(addr: int, data: bytes| list[int]):
|
|
for i, val in enumerate(data):
|
|
memory[addr + i] = int(val)
|
|
|
|
async def handle_memory(dut):
|
|
while True:
|
|
await RisingEdge(dut.clk)
|
|
addr = int(dut.AB.value)
|
|
we = bool(dut.WE.value)
|
|
|
|
dut.DI.value = memory[addr]
|
|
|
|
if we:
|
|
memory[addr] = int(dut.DO.value)
|
|
|
|
@cocotb.test
|
|
async def test_reset(dut):
|
|
cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start())
|
|
cocotb.start_soon(handle_memory(dut))
|
|
|
|
write_dword(0xfffffff4, 0x12345678)
|
|
|
|
dut.RDY.value = Immediate(1)
|
|
|
|
dut.reset.value = Immediate(1)
|
|
for _ in range(10):
|
|
await RisingEdge(dut.clk)
|
|
dut.reset.value = 0
|
|
|
|
expected_cpu_outputs = [
|
|
(0x00000100, True, (int(dut.PC.value) >> 24) & 0xff), # High addr
|
|
(0x000001ff, True, (int(dut.PC.value) >> 16) & 0xff), # Mid high addr
|
|
(0x000001fe, True, (int(dut.PC.value) >> 8) & 0xff), # Mid low addr
|
|
(0x000001fd, True, (int(dut.PC.value) >> 0) & 0xff), # Low addr
|
|
(0x000001fc, True, int(dut.P.value)), # Status
|
|
(0xfffffff4, False, int(dut.regfile.value)), # read vector byte 0
|
|
(0xfffffff5, False, int(dut.regfile.value)), # read vector byte 1
|
|
(0xfffffff6, False, int(dut.regfile.value)), # read vector byte 2
|
|
(0xfffffff7, False, int(dut.regfile.value)), # read vector byte 3
|
|
(0x12345678, False, int(dut.regfile.value)), # Read first instruction
|
|
(0x12345679, False, int(dut.regfile.value)), # Read second byte
|
|
]
|
|
|
|
for expected_output in expected_cpu_outputs:
|
|
await RisingEdge(dut.clk)
|
|
|
|
expected_addr, expected_we, expected_do = expected_output
|
|
dut_addr = int(dut.AB.value)
|
|
dut_we = bool(dut.WE.value)
|
|
dut_do = int(dut.DO.value)
|
|
|
|
assert dut_addr == expected_addr
|
|
assert dut_we == expected_we
|
|
assert dut_do == expected_do
|
|
|
|
@cocotb.test
|
|
async def test_absolute(dut):
|
|
cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start())
|
|
cocotb.start_soon(handle_memory(dut))
|
|
|
|
write_dword(0xfffffff4, 0x200)
|
|
|
|
# lda $abcd1234
|
|
# sta $50515253
|
|
# wai
|
|
write_bytes(0x200, [0xad, 0x34, 0x12, 0xcd, 0xab])
|
|
write_bytes(0x205, [0x8d, 0x53, 0x52, 0x51, 0x50])
|
|
write_byte(0x20a, 0xcb)
|
|
write_byte(0xabcd1234, 0x55)
|
|
|
|
dut.RDY.value = Immediate(1)
|
|
|
|
dut.reset.value = Immediate(1)
|
|
for _ in range(10):
|
|
await RisingEdge(dut.clk)
|
|
dut.reset.value = 0
|
|
|
|
expected_cpu_outputs = [
|
|
None, # ignore reset sequence
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
(0x00000200, False, None), # Read first instruction
|
|
(0x00000201, False, None), # Read address byte 0
|
|
(0x00000202, False, None), # Read address byte 1
|
|
(0x00000203, False, None), # Read address byte 2
|
|
(0x00000204, False, None), # Read address byte 3
|
|
(0xabcd1234, False, None), # Read from absolute address
|
|
(0x00000205, False, None), # Read second instruction
|
|
(0x00000206, False, None), # Read address byte 0
|
|
(0x00000207, False, None), # Read address byte 1
|
|
(0x00000208, False, None), # Read address byte 2
|
|
(0x00000209, False, None), # Read address byte 3
|
|
(0x50515253, True, 0x55), # Write to absolute address
|
|
]
|
|
|
|
for expected_output in expected_cpu_outputs:
|
|
await RisingEdge(dut.clk)
|
|
|
|
if expected_output:
|
|
expected_addr, expected_we, expected_do = expected_output
|
|
dut_addr = int(dut.AB.value)
|
|
dut_we = bool(dut.WE.value)
|
|
dut_do = int(dut.DO.value)
|
|
|
|
assert dut_addr == expected_addr
|
|
assert dut_we == expected_we
|
|
|
|
if dut_we:
|
|
assert dut_do == expected_do
|
|
|
|
@cocotb.test
|
|
async def test_absolute_x(dut):
|
|
cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start())
|
|
cocotb.start_soon(handle_memory(dut))
|
|
|
|
write_dword(0xfffffff4, 0x200)
|
|
|
|
# ldx #1
|
|
# lda $abcd1234,x
|
|
# inx
|
|
# sta $01020304,x
|
|
# wai
|
|
write_bytes(0x200, [0xa2, 0x01])
|
|
write_bytes(0x202, [0xbd, 0x34, 0x12, 0xcd, 0xab])
|
|
write_bytes(0x207, [0xe8])
|
|
write_bytes(0x208, [0x9d, 0x04, 0x03, 0x02, 0x01])
|
|
|
|
write_byte(0x20d, 0xcb)
|
|
write_byte(0xabcd1235, 0xaa)
|
|
|
|
dut.RDY.value = Immediate(1)
|
|
|
|
dut.reset.value = Immediate(1)
|
|
for _ in range(10):
|
|
await RisingEdge(dut.clk)
|
|
dut.reset.value = 0
|
|
|
|
expected_cpu_outputs = [
|
|
None, # ignore reset sequence
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
(0x00000200, False, None), # ldx #1
|
|
(0x00000201, False, None), # Immediate
|
|
(0x00000202, False, None), # ldx $abcd1234,x
|
|
(0x00000203, False, None), # addr 0
|
|
(0x00000204, False, None), # addr 1
|
|
(0x00000205, False, None), # addr 2
|
|
(0x00000206, False, None), # addr 3
|
|
(0xabcd1235, False, None), # Read from address
|
|
(0x00000207, False, None), # inx
|
|
(0x00000208, False, None), # sta $01020304,x
|
|
(0x00000208, False, None), # store reg
|
|
(0x00000209, False, None), # addr 0
|
|
(0x0000020a, False, None), # addr 1
|
|
(0x0000020b, False, None), # addr 2
|
|
(0x0000020c, False, None), # addr 3
|
|
(0x01020306, False, None), # Write to address
|
|
(0x01020306, True, 0xaa), # Write to address
|
|
]
|
|
|
|
for expected_output in expected_cpu_outputs:
|
|
await RisingEdge(dut.clk)
|
|
|
|
if expected_output:
|
|
expected_addr, expected_we, expected_do = expected_output
|
|
dut_addr = int(dut.AB.value)
|
|
dut_we = bool(dut.WE.value)
|
|
dut_do = int(dut.DO.value)
|
|
|
|
assert dut_addr == expected_addr
|
|
assert dut_we == expected_we
|
|
|
|
if dut_we:
|
|
assert dut_do == expected_do
|