diff --git a/hw/application_fpga/Makefile b/hw/application_fpga/Makefile index a409ec31..aad94caa 100644 --- a/hw/application_fpga/Makefile +++ b/hw/application_fpga/Makefile @@ -148,6 +148,10 @@ IRQPOC_OBJS = \ $(P)/fw/irqpoc/main.o \ $(P)/fw/irqpoc/start.o \ +IRQPOC_LED_TOGGLE_OBJS = \ + $(P)/fw/irqpoc_led_toggle/main.o \ + $(P)/fw/irqpoc_led_toggle/start.o \ + #------------------------------------------------------------------- # All: Complete build of HW and FW. #------------------------------------------------------------------- @@ -185,6 +189,7 @@ LDFLAGS = -T $(P)/fw/tk1/firmware.lds $(FIRMWARE_OBJS): $(FIRMWARE_DEPS) $(TESTFW_OBJS): $(FIRMWARE_DEPS) $(IRQPOC_OBJS): $(FIRMWARE_DEPS) +$(IRQPOC_LED_TOGGLE_OBJS): $(FIRMWARE_DEPS) firmware.elf: $(FIRMWARE_OBJS) $(P)/fw/tk1/firmware.lds $(CC) $(CFLAGS) $(FIRMWARE_OBJS) $(LDFLAGS) -o $@ @@ -230,6 +235,9 @@ testfw.elf: $(TESTFW_OBJS) $(P)/fw/tk1/firmware.lds irqpoc.elf: $(IRQPOC_OBJS) $(P)/fw/tk1/firmware.lds $(CC) $(CFLAGS) $(IRQPOC_OBJS) $(LDFLAGS) -o $@ +irqpoc_led_toggle.elf: $(IRQPOC_LED_TOGGLE_OBJS) $(P)/fw/tk1/firmware.lds + $(CC) $(CFLAGS) $(IRQPOC_LED_TOGGLE_OBJS) $(LDFLAGS) -o $@ + # Generate a fake BRAM file that will be filled in later after place-n-route bram_fw.hex: $(ICESTORM_PATH)icebram -v -g 32 $(BRAM_FW_SIZE) > $@ @@ -506,6 +514,7 @@ clean_fw: rm -f testfw.{elf,elf.map,bin,hex} rm -f $(TESTFW_OBJS) rm -f $(IRQPOC_OBJS) + rm -f $(IRQPOC_LED_TOGGLE_OBJS) rm -f qemu_firmware.elf .PHONY: clean_fw diff --git a/hw/application_fpga/fw/irqpoc_led_toggle/Makefile b/hw/application_fpga/fw/irqpoc_led_toggle/Makefile new file mode 100644 index 00000000..82b9262e --- /dev/null +++ b/hw/application_fpga/fw/irqpoc_led_toggle/Makefile @@ -0,0 +1,9 @@ +# Uses ../.clang-format +FMTFILES=main.c +.PHONY: fmt +fmt: + clang-format --dry-run --ferror-limit=0 $(FMTFILES) + clang-format --verbose -i $(FMTFILES) +.PHONY: checkfmt +checkfmt: + clang-format --dry-run --ferror-limit=0 --Werror $(FMTFILES) diff --git a/hw/application_fpga/fw/irqpoc_led_toggle/custom_ops.S b/hw/application_fpga/fw/irqpoc_led_toggle/custom_ops.S new file mode 100644 index 00000000..71889b9e --- /dev/null +++ b/hw/application_fpga/fw/irqpoc_led_toggle/custom_ops.S @@ -0,0 +1,102 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. + +#define regnum_q0 0 +#define regnum_q1 1 +#define regnum_q2 2 +#define regnum_q3 3 + +#define regnum_x0 0 +#define regnum_x1 1 +#define regnum_x2 2 +#define regnum_x3 3 +#define regnum_x4 4 +#define regnum_x5 5 +#define regnum_x6 6 +#define regnum_x7 7 +#define regnum_x8 8 +#define regnum_x9 9 +#define regnum_x10 10 +#define regnum_x11 11 +#define regnum_x12 12 +#define regnum_x13 13 +#define regnum_x14 14 +#define regnum_x15 15 +#define regnum_x16 16 +#define regnum_x17 17 +#define regnum_x18 18 +#define regnum_x19 19 +#define regnum_x20 20 +#define regnum_x21 21 +#define regnum_x22 22 +#define regnum_x23 23 +#define regnum_x24 24 +#define regnum_x25 25 +#define regnum_x26 26 +#define regnum_x27 27 +#define regnum_x28 28 +#define regnum_x29 29 +#define regnum_x30 30 +#define regnum_x31 31 + +#define regnum_zero 0 +#define regnum_ra 1 +#define regnum_sp 2 +#define regnum_gp 3 +#define regnum_tp 4 +#define regnum_t0 5 +#define regnum_t1 6 +#define regnum_t2 7 +#define regnum_s0 8 +#define regnum_s1 9 +#define regnum_a0 10 +#define regnum_a1 11 +#define regnum_a2 12 +#define regnum_a3 13 +#define regnum_a4 14 +#define regnum_a5 15 +#define regnum_a6 16 +#define regnum_a7 17 +#define regnum_s2 18 +#define regnum_s3 19 +#define regnum_s4 20 +#define regnum_s5 21 +#define regnum_s6 22 +#define regnum_s7 23 +#define regnum_s8 24 +#define regnum_s9 25 +#define regnum_s10 26 +#define regnum_s11 27 +#define regnum_t3 28 +#define regnum_t4 29 +#define regnum_t5 30 +#define regnum_t6 31 + +// x8 is s0 and also fp +#define regnum_fp 8 + +#define r_type_insn(_f7, _rs2, _rs1, _f3, _rd, _opc) \ +.word (((_f7) << 25) | ((_rs2) << 20) | ((_rs1) << 15) | ((_f3) << 12) | ((_rd) << 7) | ((_opc) << 0)) + +#define picorv32_getq_insn(_rd, _qs) \ +r_type_insn(0b0000000, 0, regnum_ ## _qs, 0b100, regnum_ ## _rd, 0b0001011) + +#define picorv32_setq_insn(_qd, _rs) \ +r_type_insn(0b0000001, 0, regnum_ ## _rs, 0b010, regnum_ ## _qd, 0b0001011) + +#define picorv32_retirq_insn() \ +r_type_insn(0b0000010, 0, 0, 0b000, 0, 0b0001011) + +#define picorv32_maskirq_insn(_rd, _rs) \ +r_type_insn(0b0000011, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011) + +#define picorv32_waitirq_insn(_rd) \ +r_type_insn(0b0000100, 0, 0, 0b100, regnum_ ## _rd, 0b0001011) + +#define picorv32_timer_insn(_rd, _rs) \ +r_type_insn(0b0000101, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011) + diff --git a/hw/application_fpga/fw/irqpoc_led_toggle/main.c b/hw/application_fpga/fw/irqpoc_led_toggle/main.c new file mode 100644 index 00000000..c98f474b --- /dev/null +++ b/hw/application_fpga/fw/irqpoc_led_toggle/main.c @@ -0,0 +1,10 @@ +/* + * Copyright (C) 2022, 2023 - Tillitis AB + * SPDX-License-Identifier: GPL-2.0-only + */ + +int main(void) +{ + while (1) { + } +} diff --git a/hw/application_fpga/fw/irqpoc_led_toggle/start.S b/hw/application_fpga/fw/irqpoc_led_toggle/start.S new file mode 100644 index 00000000..93006dea --- /dev/null +++ b/hw/application_fpga/fw/irqpoc_led_toggle/start.S @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2022, 2023 - Tillitis AB + * SPDX-License-Identifier: GPL-2.0-only +*/ + +// Triggers both interrupts, one after the other, in an endless loop. The +// interrupt handler updates the LED color depending on which interrupt +// was triggered. +// The LED color will alterate between blue and green. +// +// | IRQ number | Color | +// |------------|-------| +// | 30 | Blue | +// | 31 | Green | +// | Unexpected | Red | +// + +#include "custom_ops.S" // PicoRV32 custom instructions + + .section ".text.init" + .globl _start +_start: + j init + + + .=0x10 // IRQ handler at fixed address 0x10 +irq_handler: + // PicoRV32 stores the IRQ bitmask in x4. + // If bit 31 is 1: IRQ31 was triggered. + // If bit 30 is 1: IRQ30 was triggered. +irq30_check: + li t4, (1 << 30) + bne x4, t4, irq31_check + call led_blue + call delay + j irq_source_check_done +irq31_check: + li t4, (1 << 31) + bne x4, t4, unexpected_irq + call led_green + call delay + j irq_source_check_done +unexpected_irq: + call led_red + call delay +irq_source_check_done: + picorv32_retirq_insn() // Return from interrupt + + +init: + li t0, 0x3fffffff // IRQ31 & IRQ30 mask + picorv32_maskirq_insn(zero, t0) // Enable IRQs + +irq_trigger_loop: + li t0, 0xe0000000 // IRQ31 trigger address + sw zero, 0(t0) // Raise IRQ by writing to interrupt trigger address. + // Writing any data triggers an interrupt. + + li t0, 0xe1000000 // IRQ30 trigger address + sw zero, 0(t0) // Raise IRQ by writing to interrupt trigger address. + // Writing any data triggers an interrupt. + j irq_trigger_loop + +led_red: + li t2, 0xff000024 + li t3, (1 << 2) + sw t3, 0(t2) + ret + +led_green: + li t2, 0xff000024 + li t3, (1 << 1) + sw t3, 0(t2) + ret + +led_blue: + li t2, 0xff000024 + li t3, (1 << 0) + sw t3, 0(t2) + ret + +delay: + li t5, 0x100000 +delay_dec: + addi t5, t5, -1 + bne t5, zero, delay_dec + ret + + .align 4 // Padding to please makehex.py which requires even 4-byte file + // sizes. +