Move everything around
This commit is contained in:
29
sim/embedded_wrapper/asm_source/Makefile
Normal file
29
sim/embedded_wrapper/asm_source/Makefile
Normal file
@@ -0,0 +1,29 @@
|
||||
|
||||
|
||||
# work in progress
|
||||
.SUFFIXES:
|
||||
|
||||
|
||||
PROGRAMS=jsr_test lda_test
|
||||
|
||||
SRCS=$(wildcard *.s)
|
||||
OBJS=$(SRCS:.s=.o)
|
||||
|
||||
CA65=$(CC65_BIN)/ca65
|
||||
LD65=$(CC65_BIN)/ld65
|
||||
|
||||
CA_ARGS=--cpu 65c032
|
||||
|
||||
all: $(PROGRAMS)
|
||||
|
||||
$(PROGRAMS): $(PROGRAM=$(.TARGET))
|
||||
|
||||
%.o: %.s
|
||||
$(CA65) $(CA_ARGS) $^ -o $@ -l $@.lst
|
||||
|
||||
$(PROGRAMS): $(OBJS)
|
||||
$(LD65) -o $@ -C memory.cfg vectors.o $@.o
|
||||
|
||||
clean:
|
||||
rm -rf $(PROGRAMS) $(OBJS)
|
||||
rm -rf *.o *.lst
|
||||
BIN
sim/embedded_wrapper/asm_source/jsr_test
Normal file
BIN
sim/embedded_wrapper/asm_source/jsr_test
Normal file
Binary file not shown.
32
sim/embedded_wrapper/asm_source/jsr_test.s
Normal file
32
sim/embedded_wrapper/asm_source/jsr_test.s
Normal file
@@ -0,0 +1,32 @@
|
||||
.export vec_reset, vec_irq, vec_nmi
|
||||
|
||||
.ZEROPAGE
|
||||
|
||||
result: .res 1
|
||||
|
||||
.segment "CODE"
|
||||
|
||||
vec_nmi:
|
||||
vec_reset:
|
||||
vec_irq:
|
||||
|
||||
|
||||
jsr_test:
|
||||
lda #$ff
|
||||
sta result
|
||||
ldx #$ff
|
||||
txs
|
||||
jsr function_1
|
||||
lda #$01
|
||||
sta result
|
||||
wai
|
||||
|
||||
function_2:
|
||||
pha
|
||||
pla
|
||||
rts
|
||||
|
||||
function_1:
|
||||
jsr function_2
|
||||
rts
|
||||
|
||||
BIN
sim/embedded_wrapper/asm_source/lda_test
Normal file
BIN
sim/embedded_wrapper/asm_source/lda_test
Normal file
Binary file not shown.
121
sim/embedded_wrapper/asm_source/lda_test.s
Normal file
121
sim/embedded_wrapper/asm_source/lda_test.s
Normal file
@@ -0,0 +1,121 @@
|
||||
.export vec_reset, vec_irq, vec_nmi
|
||||
|
||||
.ZEROPAGE
|
||||
|
||||
result: .res 1
|
||||
|
||||
zp0: .res 1
|
||||
zp1: .res 4
|
||||
zp2: .res 8
|
||||
zp3: .res 4
|
||||
|
||||
good_count: .res 1
|
||||
|
||||
.CODE
|
||||
|
||||
data1: .byte 1
|
||||
data2: .byte 2
|
||||
data3: .byte 3
|
||||
data4: .byte 4
|
||||
data5: .byte 5
|
||||
data6: .res 4
|
||||
.byte 6
|
||||
data7: .res 2
|
||||
.byte 7
|
||||
|
||||
vec_nmi:
|
||||
vec_reset:
|
||||
vec_irq:
|
||||
|
||||
|
||||
prepare_test:
|
||||
lda data1
|
||||
sta zp0
|
||||
|
||||
lda #.LOBYTE(data2)
|
||||
sta zp1
|
||||
lda #.HIBYTE(data2)
|
||||
sta zp1+1
|
||||
lda #.BANKBYTE(data2)
|
||||
sta zp1+2
|
||||
lda #.TOPBYTE(data2)
|
||||
sta zp1+3
|
||||
|
||||
lda #.LOBYTE(data4)
|
||||
sta zp2+4
|
||||
lda #.HIBYTE(data4)
|
||||
sta zp2+5
|
||||
lda #.BANKBYTE(data4)
|
||||
sta zp2+6
|
||||
lda #.TOPBYTE(data4)
|
||||
sta zp2+7
|
||||
|
||||
lda #.LOBYTE(data5-2)
|
||||
sta zp3
|
||||
lda #.HIBYTE(data5-2)
|
||||
sta zp3+1
|
||||
lda #.BANKBYTE(data5-2)
|
||||
sta zp3+2
|
||||
lda #.TOPBYTE(data5-2)
|
||||
sta zp3+3
|
||||
|
||||
stz good_count
|
||||
|
||||
lda_test:
|
||||
@test1:
|
||||
lda zp0 ; data 1
|
||||
cmp #$1
|
||||
bne @test2
|
||||
inc good_count
|
||||
|
||||
@test2:
|
||||
lda (zp1) ; data 2
|
||||
cmp #$2
|
||||
bne @test3
|
||||
inc good_count
|
||||
|
||||
@test3:
|
||||
lda data3 ; data 3
|
||||
cmp #$3
|
||||
bne @test4
|
||||
inc good_count
|
||||
|
||||
@test4:
|
||||
ldx #$4
|
||||
ldy #$2
|
||||
lda (zp2,x) ; data 4
|
||||
cmp #$4
|
||||
bne @test5
|
||||
inc good_count
|
||||
|
||||
@test5:
|
||||
lda (zp3),y ; data 5
|
||||
cmp #$5
|
||||
bne @test6
|
||||
inc good_count
|
||||
|
||||
@test6:
|
||||
lda data6,x ; data 6
|
||||
cmp #$6
|
||||
bne @test7
|
||||
inc good_count
|
||||
|
||||
@test7:
|
||||
lda data7,y ; data 7
|
||||
cmp #$7
|
||||
bne @done
|
||||
inc good_count
|
||||
|
||||
@done:
|
||||
lda good_count
|
||||
cmp #$7
|
||||
bne @fail
|
||||
|
||||
lda #$1
|
||||
sta result
|
||||
wai
|
||||
|
||||
@fail:
|
||||
lda #$ff
|
||||
sta result
|
||||
wai
|
||||
13
sim/embedded_wrapper/asm_source/memory.cfg
Normal file
13
sim/embedded_wrapper/asm_source/memory.cfg
Normal file
@@ -0,0 +1,13 @@
|
||||
MEMORY {
|
||||
ZP: start = $0, size = $100;
|
||||
RAM: start = $fffff000, size = $1000, file=%O;
|
||||
}
|
||||
|
||||
SEGMENTS {
|
||||
ZEROPAGE: load = ZP, type = zp;
|
||||
CODE: load = RAM, type = ro;
|
||||
RODATA: load = RAM, type = ro;
|
||||
DATA: load = RAM, type = rw;
|
||||
BSS: load = RAM, type = bss, define = yes;
|
||||
VECTORS: load = RAM, type = ro, start = $FFFFFFF4;
|
||||
}
|
||||
7
sim/embedded_wrapper/asm_source/vectors.s
Normal file
7
sim/embedded_wrapper/asm_source/vectors.s
Normal file
@@ -0,0 +1,7 @@
|
||||
.import vec_reset, vec_irq, vec_nmi
|
||||
|
||||
.segment "VECTORS": dword
|
||||
|
||||
.addr vec_nmi
|
||||
.addr vec_reset
|
||||
.addr vec_irq
|
||||
9
sim/embedded_wrapper/verilog6502_32bit.yaml
Normal file
9
sim/embedded_wrapper/verilog6502_32bit.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
tests:
|
||||
- name: "cpu_65c02"
|
||||
toplevel: "cpu_65c02"
|
||||
modules:
|
||||
- "verilog6502_32bit_test"
|
||||
sources: "../sources.list"
|
||||
waves: True
|
||||
defines:
|
||||
SIM: "hi"
|
||||
9
sim/embedded_wrapper/verilog6502_32bit_asm.yaml
Normal file
9
sim/embedded_wrapper/verilog6502_32bit_asm.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
tests:
|
||||
- name: "cpu_65c02"
|
||||
toplevel: "cpu_65c02"
|
||||
modules:
|
||||
- "verilog6502_32bit_asm_test"
|
||||
sources: "../sources.list"
|
||||
waves: True
|
||||
defines:
|
||||
SIM: "hi"
|
||||
71
sim/embedded_wrapper/verilog6502_32bit_asm_test.py
Normal file
71
sim/embedded_wrapper/verilog6502_32bit_asm_test.py
Normal file
@@ -0,0 +1,71 @@
|
||||
import cocotb
|
||||
from cocotb.handle import Immediate
|
||||
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import Timer, RisingEdge, FallingEdge, with_timeout
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
import struct
|
||||
import random
|
||||
|
||||
import os
|
||||
|
||||
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)
|
||||
|
||||
async def do_asm_test(dut, filename):
|
||||
cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start())
|
||||
cocotb.start_soon(handle_memory(dut))
|
||||
|
||||
path = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
base_addr = 0xfffff000
|
||||
|
||||
with open(f"{path}/asm_source/{filename}", "rb") as file:
|
||||
for i, val in enumerate(file.read()):
|
||||
write_byte(base_addr+i, val)
|
||||
|
||||
dut.RDY.value = Immediate(1)
|
||||
|
||||
dut.reset.value = Immediate(1)
|
||||
for _ in range(10):
|
||||
await RisingEdge(dut.clk)
|
||||
dut.reset.value = 0
|
||||
|
||||
await with_timeout(FallingEdge(dut.RDY_O), 10, "us")
|
||||
|
||||
assert memory[0] == 1
|
||||
|
||||
@cocotb.test
|
||||
async def test_lda(dut):
|
||||
await do_asm_test(dut, "lda_test")
|
||||
|
||||
@cocotb.test
|
||||
async def test_jsr(dut):
|
||||
await do_asm_test(dut, "jsr_test")
|
||||
806
sim/embedded_wrapper/verilog6502_32bit_test.py
Normal file
806
sim/embedded_wrapper/verilog6502_32bit_test.py
Normal file
@@ -0,0 +1,806 @@
|
||||
import cocotb
|
||||
from cocotb.handle import Immediate
|
||||
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import Timer, RisingEdge, FallingEdge
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
import struct
|
||||
import random
|
||||
|
||||
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)
|
||||
|
||||
async def check_instruction_sequence(dut, instruction_sequence):
|
||||
for expected_output in instruction_sequence:
|
||||
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_reset(dut):
|
||||
cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start())
|
||||
cocotb.start_soon(handle_memory(dut))
|
||||
|
||||
write_dword(0xfffffff8, 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
|
||||
(0xfffffff8, False, int(dut.regfile.value)), # read vector byte 0
|
||||
(0xfffffff9, False, int(dut.regfile.value)), # read vector byte 1
|
||||
(0xfffffffa, False, int(dut.regfile.value)), # read vector byte 2
|
||||
(0xfffffffb, 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
|
||||
]
|
||||
|
||||
await check_instruction_sequence(dut, expected_cpu_outputs)
|
||||
|
||||
@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(0xfffffff8, 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
|
||||
]
|
||||
|
||||
await check_instruction_sequence(dut, expected_cpu_outputs)
|
||||
|
||||
|
||||
@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(0xfffffff8, 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
|
||||
]
|
||||
|
||||
await check_instruction_sequence(dut, expected_cpu_outputs)
|
||||
|
||||
|
||||
@cocotb.test
|
||||
async def test_absolute_y(dut):
|
||||
cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start())
|
||||
cocotb.start_soon(handle_memory(dut))
|
||||
|
||||
write_dword(0xfffffff8, 0x200)
|
||||
|
||||
# ldy #1
|
||||
# lda $abcd1234,y
|
||||
# iny
|
||||
# sta $01020304,y
|
||||
# wai
|
||||
write_bytes(0x200, [0xa0, 0x01])
|
||||
write_bytes(0x202, [0xb9, 0x34, 0x12, 0xcd, 0xab])
|
||||
write_bytes(0x207, [0xc8])
|
||||
write_bytes(0x208, [0x99, 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
|
||||
]
|
||||
|
||||
await check_instruction_sequence(dut, expected_cpu_outputs)
|
||||
|
||||
|
||||
@cocotb.test
|
||||
async def test_absolute_x_indirect(dut):
|
||||
cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start())
|
||||
cocotb.start_soon(handle_memory(dut))
|
||||
|
||||
write_dword(0xfffffff8, 0x200)
|
||||
|
||||
# ldx #1
|
||||
# jmp ($deadbeef,x)
|
||||
write_bytes(0x200, [0xa2, 0x01])
|
||||
write_bytes(0x202, [0x7c, 0xef, 0xbe, 0xad, 0xde])
|
||||
write_byte(0xbeefb055, 0xcb)
|
||||
|
||||
write_dword(0xdeadbeef + 1, 0xbeefb055)
|
||||
|
||||
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), # jmp ($deadbeef,x)
|
||||
(0x00000203, False, None), # addr 0
|
||||
(0x00000204, False, None), # addr 1
|
||||
(0x00000205, False, None), # addr 2
|
||||
(0x00000206, False, None), # addr 3
|
||||
(0xdeadbef0, False, None), # addr 0
|
||||
(0xdeadbef1, False, None), # addr 1
|
||||
(0xdeadbef2, False, None), # addr 2
|
||||
(0xdeadbef3, False, None), # addr 3
|
||||
(0xbeefb055, False, None), # target
|
||||
]
|
||||
|
||||
await check_instruction_sequence(dut, expected_cpu_outputs)
|
||||
|
||||
|
||||
@cocotb.test
|
||||
async def test_indirect(dut):
|
||||
cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start())
|
||||
cocotb.start_soon(handle_memory(dut))
|
||||
|
||||
write_dword(0xfffffff8, 0x200)
|
||||
|
||||
# jmp ($deadbeef)
|
||||
write_bytes(0x200, [0x6c, 0xef, 0xbe, 0xad, 0xde])
|
||||
write_byte(0xbeefb055, 0xcb)
|
||||
|
||||
write_dword(0xdeadbeef, 0xbeefb055)
|
||||
|
||||
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), # jmp ($deadbeef)
|
||||
(0x00000201, False, None), # addr 0
|
||||
(0x00000202, False, None), # addr 1
|
||||
(0x00000203, False, None), # addr 2
|
||||
(0x00000204, False, None), # addr 3
|
||||
(0xdeadbeef, False, None), # addr 0
|
||||
(0xdeadbef0, False, None), # addr 1
|
||||
(0xdeadbef1, False, None), # addr 2
|
||||
(0xdeadbef2, False, None), # addr 3
|
||||
(0xbeefb055, False, None), # target
|
||||
]
|
||||
|
||||
await check_instruction_sequence(dut, expected_cpu_outputs)
|
||||
|
||||
@cocotb.test
|
||||
async def test_indexed_indirect(dut):
|
||||
cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start())
|
||||
cocotb.start_soon(handle_memory(dut))
|
||||
|
||||
write_dword(0xfffffff8, 0x200)
|
||||
|
||||
# ldy #1
|
||||
# lda ($04),y
|
||||
# iny
|
||||
# inc
|
||||
# sta ($04),y
|
||||
write_bytes(0x200, [0xa0, 0x02])
|
||||
write_bytes(0x202, [0xb1, 0x04])
|
||||
write_bytes(0x204, [0xc8])
|
||||
write_bytes(0x205, [0x1a])
|
||||
write_bytes(0x206, [0x91, 0x04])
|
||||
write_byte(0x208, 0xcb)
|
||||
|
||||
write_dword(0x04, 0xabcdbeef)
|
||||
write_dword(0xabcdbeef+2, 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), # ldy #0
|
||||
(0x00000201, False, None), # Immediate
|
||||
(0x00000202, False, None), # lda ($04),y
|
||||
(0x00000203, False, None), # ZP index
|
||||
(0x00000004, False, None), # zp addr 0
|
||||
(0x00000005, False, None), # zp addr 1
|
||||
(0x00000006, False, None), # zp addr 2
|
||||
(0x00000007, False, None), # zp addr 3
|
||||
(0xabcdbef1, False, None), # fetch data
|
||||
(0x00000204, False, None), # iny
|
||||
(0x00000205, False, None), # iny
|
||||
(0x00000205, False, None), # inc
|
||||
(0x00000206, False, None), # inc
|
||||
(0x00000206, False, None), # sta ($04),y
|
||||
(0x00000207, False, None), # ZP index
|
||||
(0x00000004, False, None), # zp addr 0
|
||||
(0x00000005, False, None), # zp addr 1
|
||||
(0x00000006, False, None), # zp addr 2
|
||||
(0x00000007, False, None), # zp addr 3
|
||||
(0xabcdbef2, False, None), # store takes extra cycle
|
||||
(0xabcdbef2, True, 0xAB), # write data
|
||||
]
|
||||
|
||||
await check_instruction_sequence(dut, expected_cpu_outputs)
|
||||
|
||||
@cocotb.test
|
||||
async def test_indirect_indexed(dut):
|
||||
cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start())
|
||||
cocotb.start_soon(handle_memory(dut))
|
||||
|
||||
write_dword(0xfffffff8, 0x200)
|
||||
|
||||
|
||||
# ldx #4
|
||||
# lda ($04,x)
|
||||
# inc
|
||||
# ldx #8
|
||||
# sta ($04,x)
|
||||
write_bytes(0x200, [0xa2, 0x04])
|
||||
write_bytes(0x202, [0xa1, 0x04])
|
||||
write_bytes(0x204, [0x1a])
|
||||
write_bytes(0x205, [0xa2, 0x08])
|
||||
write_bytes(0x207, [0x81, 0x04])
|
||||
write_byte(0x209, 0xcb)
|
||||
|
||||
write_dword(0x08, 0xfeedf00d)
|
||||
write_dword(0x0c, 0xf00d600d)
|
||||
|
||||
write_byte(0xfeedf00d, 0x69)
|
||||
|
||||
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 #4
|
||||
(0x00000201, False, None), # Immediate
|
||||
(0x00000202, False, None), # lda ($04,x)
|
||||
(0x00000203, False, None), # ZP index
|
||||
(0x00000004, False, None), # Compute ZP index
|
||||
(0x00000008, False, None), # zp addr 0
|
||||
(0x00000009, False, None), # zp addr 1
|
||||
(0x0000000a, False, None), # zp addr 2
|
||||
(0x0000000b, False, None), # zp addr 3
|
||||
(0xfeedf00d, False, None), # fetch data
|
||||
(0x00000204, False, None), # iny
|
||||
(0x00000205, False, None), # iny
|
||||
(0x00000205, False, None), # inc
|
||||
(0x00000206, False, None), # inc
|
||||
(0x00000207, False, None), # sta ($04),y
|
||||
(0x00000208, False, None), # ZP index
|
||||
(0x00000004, False, None), # Compute ZP index
|
||||
(0x0000000c, False, None), # zp addr 0
|
||||
(0x0000000d, False, None), # zp addr 1
|
||||
(0x0000000e, False, None), # zp addr 2
|
||||
(0x0000000f, False, None), # zp addr 3
|
||||
(0xf00d600d, True, 0x6a), # write data
|
||||
]
|
||||
|
||||
await check_instruction_sequence(dut, expected_cpu_outputs)
|
||||
|
||||
@cocotb.test
|
||||
async def test_jsr(dut):
|
||||
cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start())
|
||||
cocotb.start_soon(handle_memory(dut))
|
||||
|
||||
write_dword(0xfffffff8, 0x200)
|
||||
|
||||
# @0x200
|
||||
# ldx #$0
|
||||
# txs
|
||||
# jsr $12345678
|
||||
# wai
|
||||
#
|
||||
# @0x1234
|
||||
# rts
|
||||
write_bytes(0x200, [0xa2, 0xff, 0x9a, 0x20, 0x78, 0x56, 0x34, 0x12, 0xcb])
|
||||
write_bytes(0x12345678, [0x60])
|
||||
|
||||
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 #$00
|
||||
(0x00000201, False, None), # Immediate
|
||||
(0x00000202, False, None), # txs
|
||||
(0x00000203, False, None), # second cycle of txs
|
||||
(0x00000203, False, None), # jsr $12345678
|
||||
(0x00000204, False, None), # first byte of address
|
||||
(0x000001ff, True, 0x00), # 24-31
|
||||
(0x000001fe, True, 0x00), # 16-23
|
||||
(0x000001fd, True, 0x02), # 8-15
|
||||
(0x000001fc, True, 0x05), # 7-0
|
||||
(0x00000205, False, None), # second byte of address
|
||||
(0x00000206, False, None), # third byte of address
|
||||
(0x00000207, False, None), # fourth byte of address
|
||||
(0x00000208, False, None), # receive last byte of address
|
||||
(0x12345678, False, None), # rts
|
||||
(0x12345679, False, None), # rts
|
||||
(0x000001fb, False, None), # current stack while we add 1 to it
|
||||
(0x000001fc, False, None), # 7-0
|
||||
(0x000001fd, False, None), # 15-8
|
||||
(0x000001fe, False, None), # 23-16
|
||||
(0x000001ff, False, None), # 31-24
|
||||
(0x1234567c, False, None), # Updating PC before jump
|
||||
(0x00000208, False, None), # WAI
|
||||
(0x00000209, False, None), # second wai
|
||||
]
|
||||
|
||||
await check_instruction_sequence(dut, expected_cpu_outputs)
|
||||
|
||||
@cocotb.test
|
||||
async def test_rti(dut):
|
||||
cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start())
|
||||
cocotb.start_soon(handle_memory(dut))
|
||||
|
||||
write_dword(0xfffffff8, 0x200)
|
||||
write_dword(0xfffffffc, 0x300)
|
||||
|
||||
|
||||
# @0x200
|
||||
# ldx #$ff
|
||||
# txs
|
||||
# brk
|
||||
# wai
|
||||
# @0x300
|
||||
# rti
|
||||
|
||||
write_bytes(0x200, [0xa2, 0xff, 0x9a, 0x00, 0x00, 0xcb]) # BRK is technically a 2 byte instruction
|
||||
write_bytes(0x300, [0x40])
|
||||
|
||||
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 #$ff
|
||||
(0x00000201, False, None), # immediate
|
||||
(0x00000202, False, None), # txs
|
||||
(0x00000203, False, None), # txs
|
||||
(0x00000203, False, None), # brk
|
||||
(0x00000204, False, None), # brk
|
||||
(0x000001ff, True, 0x00), # brk 31-24
|
||||
(0x000001fe, True, 0x00), # brk 13-16
|
||||
(0x000001fd, True, 0x02), # brk 15-08
|
||||
(0x000001fc, True, 0x05), # brk 07-00
|
||||
(0x000001fb, True, 0xb4), # brk flags
|
||||
(0xfffffffc, False, None), # vector
|
||||
(0xfffffffd, False, None), # vector
|
||||
(0xfffffffe, False, None), # vector
|
||||
(0xffffffff, False, None), # vector
|
||||
(0x00000300, False, None), # rti
|
||||
(0x00000301, False, None), # rti
|
||||
(0x000001fa, False, None), # rti
|
||||
(0x000001fb, False, None), # rti
|
||||
(0x000001fc, False, None), # rti
|
||||
(0x000001fd, False, None), # rti
|
||||
(0x000001fe, False, None), # rti
|
||||
(0x000001ff, False, None), # rti
|
||||
(0x00000205, False, None), # wai
|
||||
]
|
||||
|
||||
await check_instruction_sequence(dut, expected_cpu_outputs)
|
||||
|
||||
@cocotb.test
|
||||
async def test_irq(dut):
|
||||
cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start())
|
||||
cocotb.start_soon(handle_memory(dut))
|
||||
|
||||
write_dword(0xfffffff8, 0x200)
|
||||
write_dword(0xfffffffc, 0x300)
|
||||
|
||||
# @0x200
|
||||
# cli
|
||||
# wai
|
||||
# wai
|
||||
# 0x300
|
||||
# rti
|
||||
|
||||
write_bytes(0x200, [0x58, 0xcb, 0xcb])
|
||||
write_bytes(0x300, [0x40])
|
||||
|
||||
dut.RDY.value = Immediate(1)
|
||||
|
||||
dut.reset.value = Immediate(1)
|
||||
for _ in range(10):
|
||||
await RisingEdge(dut.clk)
|
||||
dut.reset.value = 0
|
||||
|
||||
await FallingEdge(dut.RDY_O)
|
||||
|
||||
await RisingEdge(dut.clk)
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
dut.IRQ.value = 1
|
||||
|
||||
while True:
|
||||
await RisingEdge(dut.clk)
|
||||
if int(dut.state.value) == 0x08:
|
||||
break
|
||||
|
||||
dut.IRQ.value = 0
|
||||
|
||||
await Timer(300, "ns")
|
||||
|
||||
assert int(dut.RDY_O.value) == 0
|
||||
|
||||
@cocotb.test
|
||||
async def test_bra_always(dut):
|
||||
cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start())
|
||||
cocotb.start_soon(handle_memory(dut))
|
||||
|
||||
write_dword(0xfffffff8, 0xfffffd)
|
||||
|
||||
# L1: bra L1 0x80 0x03
|
||||
# nop
|
||||
# nop
|
||||
# L2: bra L2
|
||||
|
||||
write_bytes(0xfffffd, [0x80, 0x3, 0x00, 0x00, 0x00, 0x80, 0xfe])
|
||||
|
||||
dut.RDY.value = Immediate(1)
|
||||
|
||||
dut.reset.value = Immediate(1)
|
||||
for _ in range(10):
|
||||
await RisingEdge(dut.clk)
|
||||
dut.reset.value = 0
|
||||
|
||||
count = 0
|
||||
|
||||
async def count_address():
|
||||
nonlocal count
|
||||
while True:
|
||||
await RisingEdge(dut.clk)
|
||||
if int(dut.AB.value) == 0x01000004:
|
||||
count+=1
|
||||
|
||||
cocotb.start_soon(count_address())
|
||||
|
||||
await Timer(1, "us")
|
||||
|
||||
assert count > 0
|
||||
|
||||
@cocotb.test
|
||||
async def test_bra_never(dut):
|
||||
cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start())
|
||||
cocotb.start_soon(handle_memory(dut))
|
||||
|
||||
write_dword(0xfffffff8, 0xfffffd)
|
||||
|
||||
# lda #$00
|
||||
# bne <anywhere>
|
||||
# wai
|
||||
|
||||
write_bytes(0xfffffd, [0xa9, 0x00, 0xd0, 0x7f, 0xd0, 0x80, 0xd0, 0xfe, 0xcb])
|
||||
|
||||
|
||||
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,
|
||||
(0x00fffffd, False, None),
|
||||
(0x00fffffe, False, None),
|
||||
(0x00ffffff, False, None),
|
||||
(0x01000000, False, None),
|
||||
(0x01000001, False, None),
|
||||
(0x01000002, False, None),
|
||||
(0x01000003, False, None),
|
||||
(0x01000004, False, None),
|
||||
(0x01000005, False, None),
|
||||
(0x01000006, False, None),
|
||||
(0x01000006, False, None),
|
||||
(0x01000006, False, None),
|
||||
]
|
||||
|
||||
await check_instruction_sequence(dut, expected_cpu_outputs)
|
||||
|
||||
@cocotb.test
|
||||
async def test_adc(dut):
|
||||
cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start())
|
||||
cocotb.start_soon(handle_memory(dut))
|
||||
|
||||
write_dword(0xfffffff8, 0x200)
|
||||
|
||||
|
||||
def zp_indirect():
|
||||
value = random.randint(0,255)
|
||||
address = random.randint(0x300, 0xffffffff)
|
||||
|
||||
address_le = struct.pack("<I", address)
|
||||
|
||||
# lda #$0
|
||||
# adc ($00)
|
||||
# wai
|
||||
write_bytes(0x200, [0xa9, 0x00, 0x72, 0x00, 0xcb])
|
||||
|
||||
write_bytes(0x00, address_le)
|
||||
write_byte(address, value)
|
||||
|
||||
return value
|
||||
|
||||
def absolute():
|
||||
value = random.randint(0,255)
|
||||
address = random.randint(0x300, 0xffffffff)
|
||||
|
||||
address_le = struct.pack("<I", address)
|
||||
|
||||
# lda $#0
|
||||
# adc $address
|
||||
# wai
|
||||
|
||||
write_bytes(0x200, [0xa9, 0x00, 0x6d])
|
||||
write_bytes(0x203, address_le)
|
||||
write_bytes(0x207, [0xcb])
|
||||
|
||||
write_byte(address, value)
|
||||
|
||||
return value
|
||||
|
||||
def indirect_x():
|
||||
value = random.randint(0,255)
|
||||
address = random.randint(0x300, 0xffffff00)
|
||||
x_offset = random.randint(0,250)
|
||||
|
||||
address_le = struct.pack("<I", address)
|
||||
|
||||
# lda #$0
|
||||
# ldx #x_offset
|
||||
# adc ($00,x)
|
||||
# wai
|
||||
|
||||
write_bytes(0x200, [0xa9, 0x00, 0xa2, x_offset, 0x61, 0x00, 0xcb])
|
||||
write_bytes(x_offset, address_le)
|
||||
write_byte(address, value)
|
||||
|
||||
return value
|
||||
|
||||
tests = [zp_indirect, absolute, indirect_x]
|
||||
|
||||
for test in tests:
|
||||
value = test()
|
||||
|
||||
dut.RDY.value = Immediate(1)
|
||||
|
||||
dut.reset.value = Immediate(1)
|
||||
for _ in range(10):
|
||||
await RisingEdge(dut.clk)
|
||||
dut.reset.value = 0
|
||||
|
||||
await FallingEdge(dut.RDY_O)
|
||||
|
||||
assert value == int(dut.A.value)
|
||||
9
sim/embedded_wrapper/verilog6502_wrapper.yaml
Normal file
9
sim/embedded_wrapper/verilog6502_wrapper.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
tests:
|
||||
- name: "verilog6502_wrapper"
|
||||
toplevel: "verilog6502_wrapper_tb"
|
||||
modules:
|
||||
- "verilog6502_wrapper_test"
|
||||
sources: "../sources.list"
|
||||
waves: True
|
||||
defines:
|
||||
SIM: "hi"
|
||||
30
sim/embedded_wrapper/verilog6502_wrapper_tb.sv
Normal file
30
sim/embedded_wrapper/verilog6502_wrapper_tb.sv
Normal file
@@ -0,0 +1,30 @@
|
||||
module verilog6502_wrapper_tb();
|
||||
|
||||
`define SIM
|
||||
|
||||
taxi_apb_if s_apb();
|
||||
taxi_axil_if m_axil();
|
||||
taxi_axi_if s_axi();
|
||||
|
||||
logic clk;
|
||||
logic rst;
|
||||
|
||||
logic o_irq_ext;
|
||||
logic i_irq_ext;
|
||||
logic i_nmi_ext;
|
||||
|
||||
|
||||
verilog6502_embedded_wrapper u_dut(
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.s_apb(s_apb),
|
||||
.m_axil_rd(m_axil),
|
||||
.m_axil_wr(m_axil),
|
||||
.s_axi_rd(s_axi),
|
||||
.s_axi_wr(s_axi),
|
||||
.o_irq_ext(o_irq_ext),
|
||||
.i_irq_ext(i_irq_ext),
|
||||
.i_nmi_ext(i_nmi_ext)
|
||||
);
|
||||
|
||||
endmodule
|
||||
49
sim/embedded_wrapper/verilog6502_wrapper_test.py
Normal file
49
sim/embedded_wrapper/verilog6502_wrapper_test.py
Normal file
@@ -0,0 +1,49 @@
|
||||
import cocotb
|
||||
from cocotb.handle import Immediate
|
||||
|
||||
from cocotb.clock import Clock
|
||||
from cocotb.triggers import Timer, RisingEdge
|
||||
|
||||
from cocotbext.axi.apb import ApbMaster, ApbBus
|
||||
from cocotbext.axi import AxiMaster, AxiBus, AxiLiteBus, AxiLiteRam
|
||||
|
||||
|
||||
|
||||
CLK_PERIOD = 5
|
||||
|
||||
|
||||
@cocotb.test
|
||||
async def test_sanity(dut):
|
||||
print("Hello world")
|
||||
cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start())
|
||||
|
||||
s_apb = ApbMaster(ApbBus.from_prefix(dut.s_apb, ""), dut.clk, dut.rst)
|
||||
|
||||
s_axi = AxiMaster(AxiBus.from_prefix(dut.s_axi, ""), dut.clk, dut.rst)
|
||||
m_axil = AxiLiteRam(AxiLiteBus.from_prefix(dut.m_axil, ""), dut.clk, dut.rst, size=2**32)
|
||||
|
||||
m_axil.write(0, b"Hello, world!")
|
||||
|
||||
|
||||
dut.rst.value = Immediate(1)
|
||||
for _ in range(10):
|
||||
await RisingEdge(dut.clk)
|
||||
dut.rst.value = 0
|
||||
for _ in range(10):
|
||||
await RisingEdge(dut.clk)
|
||||
|
||||
# await s_axi.write(0x200, [0x58, 0xa9, 0x00, 0x1a, 0xcb, 0x4c, 0x03, 0x02])
|
||||
# await s_axi.write(0x200, [0xAD, 0x00, 0xE0, 0xAD, 0x01, 0xE0, 0xAD, 0x02, 0xE0, 0xAD, 0x03, 0xE0, 0xAD, 0x04, 0xE0, 0xCB])
|
||||
await s_axi.write(0x200, [0x80, 0xfe])
|
||||
|
||||
cocotb.start_soon(s_axi.read(0x200, 8))
|
||||
|
||||
await Timer(10, "us")
|
||||
|
||||
await s_apb.write_dword(0x0, 0)
|
||||
|
||||
await Timer(1, "us")
|
||||
|
||||
dut.i_nmi_ext.value = Immediate(1)
|
||||
|
||||
await Timer(1, "us")
|
||||
Reference in New Issue
Block a user