Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sw: add 'uart_tx_timer_irq' example (with UART). #76

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions mtkcpu/cpu/cpu.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,13 +251,12 @@ def elaborate(self, platform):
mtime = self.mtime = Signal(32)
sync += mtime.eq(mtime + 1)
comb += csr_unit.mtime.as_view().eq(mtime)
timer_irq_happened = Signal()

# with m.If(csr_unit.mstatus.mie & csr_unit.mie.mtie):
# with m.If(mtime == csr_unit.mtimecmp):
# # 'halt' signal needs to be cleared when CPU jumps to trap handler.
# sync += [
# self.halt.eq(1),
# ]
# Timer IRQ delivery.
with m.If(csr_unit.mstatus.as_view().mie & csr_unit.mie.as_view().mtie):
with m.If(mtime == csr_unit.mtimecmp.as_view().as_value()):
sync += timer_irq_happened.eq(1)

def prev(sig: Signal) -> Signal:
res = Signal()
Expand Down Expand Up @@ -464,6 +463,13 @@ def fetch_with_new_pc(pc : Signal):
# NOTE: 'Elif' is not accidental here - HALTREQ has higher priority than STEP.
sync += dcsr.as_view().cause.eq(DCSR_DM_Entry_Cause.STEP)
m.next = "HALTED"
with m.Elif(timer_irq_happened):
# TODO from specs:
# "The interrupt remains posted until it clears by writing the MTIMECMP register"
#
# For now we don't implement that part of specs, just clear the bit by default.
sync += timer_irq_happened.eq(0) # clear the bit
trap(IrqCause.M_TIMER_INTERRUPT, interrupt=True)
with m.Else():
# maybe next time..
m.next = "FETCH"
Expand Down
111 changes: 110 additions & 1 deletion mtkcpu/tests/test_csr.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,8 +283,117 @@
),
]

EXDI = [
MemTestCase(
name="timer interrupt happens with proper mcause",
source_type=MemTestSourceType.RAW,
source=f"""
start:
la x5, trap
csrw mtvec, x5
csrr x2, {int(CSRNonStandardIndex.MTIME)}
addi x2, x2, 128 # interupt in ~100 cycles
csrw {int(CSRNonStandardIndex.MTIMECMP)}, x2

li x1, 0b10000000 # mie.mtie
csrw mie, x1

li x1, 0b1000 # mstatus.mie
csrw mstatus, x1
loop:
j loop
trap:
csrr x14, mcause
andi x15, x14, 0xff
""",
out_reg=15,
out_val=IrqCause.M_TIMER_INTERRUPT,
timeout=2000,
mem_init=MemoryContents.empty(),
reg_init=RegistryContents.fill(),
),

# "The interrupt remains posted until it clears by writing the MTIMECMP register" - privileged specs.
MemTestCase(
name="timer interrupt is cleared only with 'mtimecmp' write",
source_type=MemTestSourceType.RAW,
source=f"""
start:
la x5, trap
csrw mtvec, x5
csrr x2, {int(CSRNonStandardIndex.MTIME)}
addi x2, x2, 128 # interupt in ~100 cycles
csrw {int(CSRNonStandardIndex.MTIMECMP)}, x2

li x1, 0b10000000 # mie.mtie
csrw mie, x1

li x1, 0b1000 # mstatus.mie
csrw mstatus, x1
loop:
j loop

trap:

// error codes on too_bad entry:
// 1 - NON-TIMER TRAP
// 2 - UNREACHABLE CODE
// 3 - MRET CLEARS TIMER IRQ (IT SHOULDN'T, DUE TO SPECS.)


// filter out non-timer related traps.
csrr x5, mcause
andi x5, x5, 0xff
li x6, {IrqCause.M_TIMER_INTERRUPT}

li x20, 1
bne x5, x6, too_bad

addi x10, x10, 1

li x11, 1
beq x10, x11, 1f
li x11, 2
beq x10, x11, 2f
li x11, 3
beq x10, x11, 3f

add x20, x0, x10
// li x20, 2
jump too_bad, x3 // should not be reached

1:
li x20, 3
la x5, too_bad // should not be executed, as we haven't yet written to 'mtimecmp'.
csrw mepc, x5
mret

2:
csrr x1, {int(CSRNonStandardIndex.MTIME)}
addi x1, x0, 1000
csrw {int(CSRNonStandardIndex.MTIMECMP)}, x1
la x5, loop
csrw mepc, x5
mret

3:
jump ok, x3

too_bad: // exit code in x20
addi x15, x20, 0
ok:
addi x15, x0, 100
""",
out_reg=15,
out_val=100,
timeout=2000,
mem_init=MemoryContents.empty(),
reg_init=RegistryContents.empty(),
),
]


@mem_test(CSR_TESTS)
# @mem_test(CSR_TESTS)
@mem_test(EXDI)
def test_registers(_):
pass
2 changes: 1 addition & 1 deletion mtkcpu/units/csr/csr_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ def elaborate(self, _):
return self.latch_whole_value_with_no_side_effect()

class MIP(CSR_Write_Handler):
addr = CSRIndex.MIE
addr = CSRIndex.MIP
layout = MIP_Layout

# TODO
Expand Down
4 changes: 2 additions & 2 deletions mtkcpu/units/exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ def elaborate(self, platform):
mip.meip.eq(self.external_interrupt)
]
m.d.sync += [
# self.mstatus.r.mpie.eq(self.mstatus.r.mie),
# self.mstatus.r.mie.eq(0),
mstatus.mpie.eq(mstatus.mie),
mstatus.mie.eq(0),
mepc.eq(self.m_pc)
]
with m.If(~trap_pe.n):
Expand Down
9 changes: 9 additions & 0 deletions mtkcpu/units/mmio/bspgen.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,13 @@ def gen_bsp_sources(owners : List['BusSlaveOwnerInterface'], addr_schemes : List
__class__.__generate_cc(c, s)
log(f"generating {__class__.get_h_path(s)}")
__class__.__generate_h(c, s)
csr_path = Path(f"{__class__.gen_dir}/csr.h")

log(f"generating {csr_path}")
csr_text_lines = ["// autogenerated by 'gen_bsp' pass"]
from mtkcpu.cpu.priv_isa import CSRNonStandardIndex
for x in CSRNonStandardIndex:
csr_text_lines.append(f"#define {x.name}_CSR_REG_ADDR {hex(x.value)}")

csr_path.write_text("\n".join(csr_text_lines))
log("ok, code generation done!")
5 changes: 3 additions & 2 deletions mtkcpu/utils/tests/sim_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ def reg_test(timeout=default_timeout_extra + timeout_cycles, expected_val=expect
yield Tick()
yield Settle()

for _ in range(timeout):
for i in range(timeout):
en = yield cpu.reg_write_port.en
if en == 1:
addr = yield cpu.reg_write_port.addr
Expand All @@ -311,9 +311,10 @@ def reg_test(timeout=default_timeout_extra + timeout_cycles, expected_val=expect
if check_reg_content and cond:
# TODO that mechanism for now allows for only one write to observed register per test,
# extend it if neccessary.
pc = yield cpu.pc
print(
f"== ERROR: Expected data write to reg x{addr} of value {hex(expected_val)},"
f" got value {hex(val)}.. \n== fail test: {name}\n"
f" got value {hex(val)} in cycle={i}, PC={hex(pc)}.. \n== fail test: {name}\n"
)
print(
f"{format(expected_val, '32b')} vs {format(val, '32b')}"
Expand Down
41 changes: 22 additions & 19 deletions mtkcpu/utils/tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ def reg_test(
sim.add_sync_process(capture_write_transactions(cpu=cpu, dict_reference=result_mem))
# sim.add_sync_process(print_mem_transactions(cpu=cpu))
sim.add_sync_process(check_addr_translation_errors(cpu=cpu))

sim.add_sync_process(monitor_pc_and_main_fsm(cpu=cpu, wait_for_first_haltreq=False, log_fn=print))

sim.add_sync_process(
get_sim_register_test(
Expand All @@ -234,7 +236,7 @@ def reg_test(
# *csr_unit.mepc.fields.values(),
# *csr_unit.mcause.members.values(),
# *csr_unit.satp.fields.values(),
# *csr_unit.mie.fields.values(),
# csr_unit.mie.as_view().mtie,
# *csr_unit.mstatus.fields.values(),
# *csr_unit.mtime.fields.values(),
# *csr_unit.mtimecmp.fields.values(),
Expand All @@ -247,23 +249,23 @@ def reg_test(
# csr_unit.vld,
# csr_unit.ONREAD,
# csr_unit.ONWRITE,
cpu.arbiter.pe.i,
cpu.arbiter.pe.o,
cpu.arbiter.pe.none,
cpu.arbiter.bus_free_to_latch,

cpu.arbiter.error_code,
cpu.arbiter.addr_translation_en,
cpu.arbiter.translation_ack,
cpu.arbiter.start_translation,
cpu.arbiter.phys_addr,
cpu.arbiter.root_ppn,

*cpu.arbiter.pte.fields.values(),

cpu.arbiter.generic_bus.addr,
cpu.arbiter.generic_bus.read_data,
cpu.arbiter.vpn,
# cpu.arbiter.pe.i,
# cpu.arbiter.pe.o,
# cpu.arbiter.pe.none,
# cpu.arbiter.bus_free_to_latch,

# cpu.arbiter.error_code,
# cpu.arbiter.addr_translation_en,
# cpu.arbiter.translation_ack,
# cpu.arbiter.start_translation,
# cpu.arbiter.phys_addr,
# cpu.arbiter.root_ppn,

# *cpu.arbiter.pte.fields.values(),

# cpu.arbiter.generic_bus.addr,
# cpu.arbiter.generic_bus.read_data,
# cpu.arbiter.vpn,
]

# from amaranth.back import verilog
Expand Down Expand Up @@ -292,7 +294,8 @@ def get_code_mem(case: MemTestCase, mem_size_kb: int) -> MemoryContents:
import tempfile
with tempfile.NamedTemporaryFile(
suffix=".elf",
dir=Path(__file__).parent
dir=Path(__file__).parent,
delete=False
) as tmp_elf:
source = f"""
.global start
Expand Down
Loading