diff --git a/hw/fpga/.gitignore b/hw/fpga/.gitignore index cb37f22..42fd173 100644 --- a/hw/fpga/.gitignore +++ b/hw/fpga/.gitignore @@ -62,6 +62,7 @@ # ignore Quartus II generated folders */db/ +greybox_tmp/ */incremental_db/ */*/simulation/ */timing/ diff --git a/hw/fpga/addr_decode.sv b/hw/fpga/addr_decode.sv index 83f3c57..8d7da6a 100644 --- a/hw/fpga/addr_decode.sv +++ b/hw/fpga/addr_decode.sv @@ -3,12 +3,14 @@ module addr_decode( output logic ram_cs, output logic rom_cs, output logic hex_cs, - output logic uart_cs + output logic uart_cs, + output logic irq_cs ); assign rom_cs = addr[15]; assign ram_cs = ~addr[15] && addr < 16'h7ff0; assign hex_cs = addr >= 16'h7ff0 && addr < 16'h7ff4; assign uart_cs = addr >= 16'h7ff4 && addr < 16'h7ff6; +assign irq_cs = addr == 16'h7fff; endmodule diff --git a/hw/fpga/greybox_tmp/cbx_args.txt b/hw/fpga/greybox_tmp/cbx_args.txt deleted file mode 100644 index b308009..0000000 --- a/hw/fpga/greybox_tmp/cbx_args.txt +++ /dev/null @@ -1,16 +0,0 @@ -ADDRESS_ACLR_A=NONE -CLOCK_ENABLE_INPUT_A=BYPASS -CLOCK_ENABLE_OUTPUT_A=BYPASS -INIT_FILE=../../sw/bootrom.hex -INTENDED_DEVICE_FAMILY="MAX 10" -NUMWORDS_A=32768 -OPERATION_MODE=ROM -OUTDATA_ACLR_A=NONE -OUTDATA_REG_A=UNREGISTERED -WIDTHAD_A=15 -WIDTH_A=8 -WIDTH_BYTEENA_A=1 -DEVICE_FAMILY="MAX 10" -address_a -clock0 -q_a diff --git a/hw/fpga/hvl/cs_testbench.sv b/hw/fpga/hvl/cs_testbench.sv index b764c0f..d89ce93 100644 --- a/hw/fpga/hvl/cs_testbench.sv +++ b/hw/fpga/hvl/cs_testbench.sv @@ -9,6 +9,7 @@ logic ram_cs; logic rom_cs; logic hex_cs; logic uart_cs; +logic irq_cs; int cs_count = ram_cs + rom_cs + hex_cs + uart_cs; @@ -32,11 +33,16 @@ initial begin : TEST_VECTORS else $error("Bad CS! addr=%4x should have hex_cs!", addr); end - if (i >= 16'h7ff4 && i < 16'6) begin + if (i >= 16'h7ff4 && i < 16'h7ff6) begin assert(uart_cs == '1) else $error("Bad CS! addr=%4x should have uart_cs!", addr); end + if (i == 16'h7fff) begin + assert(irq_cs == '1) + else + $error("Bad CS! addr=%4x should have irq_cs!", addr); + end if (i >= 2**15) begin assert(rom_cs == '1) else diff --git a/hw/fpga/hvl/irq_testbench.sv b/hw/fpga/hvl/irq_testbench.sv new file mode 100644 index 0000000..8c0534d --- /dev/null +++ b/hw/fpga/hvl/irq_testbench.sv @@ -0,0 +1,75 @@ +module testbench(); + +timeunit 10ns; + +timeprecision 1ns; + +logic clk_50, rst_n, button_1; +logic [15:0] cpu_addr; +wire [7:0] cpu_data; +logic [7:0] cpu_data_in; +logic [7:0] cpu_data_out; +logic cpu_vpb, cpu_mlb, cpu_rwb, cpu_sync; +logic cpu_led, cpu_resb, cpu_rdy, cpu_sob, cpu_irqb; +logic cpu_phi2, cpu_be, cpu_nmib; +logic [6:0] HEX0, HEX1, HEX2, HEX3, HEX4, HEX5; +logic UART_RXD, UART_TXD; + +assign cpu_data = ~cpu_rwb ? cpu_data_out : 'z; +assign cpu_data_in = cpu_data; + +super6502 dut(.*); + +always #1 clk_50 = clk_50 === 1'b0; +always #100 dut.clk = dut.clk === 1'b0; + +always @(posedge dut.clk) begin + dut.cpu_phi2 <= ~dut.cpu_phi2; +end + +logic [7:0] _tmp_data; + +initial begin + rst_n <= '0; + cpu_addr <= 16'h7fff; + cpu_rwb <= '1; + button_1 <= '1; + + repeat(10) @(posedge dut.clk); + rst_n <= '1; + + repeat(10) @(posedge dut.clk); + + button_1 <= '0; + @(posedge dut.clk); + button_1 <= '1; + @(posedge dut.clk); + + assert(cpu_data[0] == '1) + else begin + $error("IRQ location should have bit 1 set!"); + end + + @(posedge dut.clk); + _tmp_data <= cpu_data_in; + @(posedge dut.clk); + + _tmp_data <= _tmp_data & ~8'b1; + + @(posedge dut.clk); + + cpu_data_out <= _tmp_data; + cpu_rwb <= '0; + + @(posedge dut.clk); + + cpu_rwb <= '1; + + repeat (5) @(posedge dut.clk); + + $finish(); + + +end + +endmodule diff --git a/hw/fpga/simulation/modelsim/irq_testbench.do b/hw/fpga/simulation/modelsim/irq_testbench.do new file mode 100644 index 0000000..7b29453 --- /dev/null +++ b/hw/fpga/simulation/modelsim/irq_testbench.do @@ -0,0 +1,24 @@ +transcript on +if {[file exists rtl_work]} { + vdel -lib rtl_work -all +} +vlib rtl_work +vmap work rtl_work + +vlog -sv -work work {../../hvl/irq_testbench.sv} +vlog -vlog01compat -work work +incdir+/home/byron/Projects/super6502/hw/fpga {/home/byron/Projects/super6502/hw/fpga/ram.v} +vlog -vlog01compat -work work +incdir+/home/byron/Projects/super6502/hw/fpga {/home/byron/Projects/super6502/hw/fpga/rom.v} +vlog -vlog01compat -work work +incdir+/home/byron/Projects/super6502/hw/fpga {/home/byron/Projects/super6502/hw/fpga/cpu_clk.v} +vlog -vlog01compat -work work +incdir+/home/byron/Projects/super6502/hw/fpga/db {/home/byron/Projects/super6502/hw/fpga/db/cpu_clk_altpll.v} +vlog -sv -work work +incdir+/home/byron/Projects/super6502/hw/fpga {/home/byron/Projects/super6502/hw/fpga/uart.sv} +vlog -sv -work work +incdir+/home/byron/Projects/super6502/hw/fpga {/home/byron/Projects/super6502/hw/fpga/addr_decode.sv} +vlog -sv -work work +incdir+/home/byron/Projects/super6502/hw/fpga {/home/byron/Projects/super6502/hw/fpga/super6502.sv} +vlog -sv -work work +incdir+/home/byron/Projects/super6502/hw/fpga {/home/byron/Projects/super6502/hw/fpga/HexDriver.sv} +vlog -sv -work work +incdir+/home/byron/Projects/super6502/hw/fpga {/home/byron/Projects/super6502/hw/fpga/SevenSeg.sv} + +vsim -t 1ps -L altera_ver -L lpm_ver -L sgate_ver -L altera_mf_ver -L altera_lnsim_ver -L stratixv_ver -L stratixv_hssi_ver -L stratixv_pcie_hip_ver -L rtl_work -L work -voptargs="+acc" testbench + +add wave -group {dut} -radix hexadecimal sim:/testbench/dut/* + +onfinish stop +run -all diff --git a/hw/fpga/super6502.qsf b/hw/fpga/super6502.qsf index 496716f..258c17c 100644 --- a/hw/fpga/super6502.qsf +++ b/hw/fpga/super6502.qsf @@ -101,7 +101,6 @@ set_location_assignment PIN_A13 -to SW[6] set_location_assignment PIN_A14 -to SW[7] set_location_assignment PIN_B14 -to SW[8] set_location_assignment PIN_F15 -to SW[9] -set_location_assignment PIN_A7 -to Run set_location_assignment PIN_A8 -to LED[0] set_location_assignment PIN_A9 -to LED[1] set_location_assignment PIN_A10 -to LED[2] @@ -272,7 +271,6 @@ set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SW[6] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SW[7] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SW[8] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SW[9] -set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to Run set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LED[0] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LED[1] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LED[2] @@ -283,4 +281,6 @@ set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LED[6] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LED[7] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LED[8] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to LED[9] +set_location_assignment PIN_A7 -to button_1 +set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to button_1 set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top \ No newline at end of file diff --git a/hw/fpga/super6502.sv b/hw/fpga/super6502.sv index 54bcf61..c564854 100644 --- a/hw/fpga/super6502.sv +++ b/hw/fpga/super6502.sv @@ -2,6 +2,7 @@ module super6502( input clk_50, input logic rst_n, + input logic button_1, input logic [15:0] cpu_addr, inout logic [7:0] cpu_data, @@ -41,11 +42,13 @@ assign cpu_data = cpu_rwb ? cpu_data_out : 'z; logic [7:0] rom_data_out; logic [7:0] ram_data_out; logic [7:0] uart_data_out; +logic [7:0] irq_data_out; logic ram_cs; logic rom_cs; logic hex_cs; logic uart_cs; +logic irq_cs; cpu_clk cpu_clk( .inclk0(clk_50), @@ -61,14 +64,15 @@ assign cpu_sob = '0; assign cpu_resb = rst_n; assign cpu_be = '1; assign cpu_nmib = '1; -assign cpu_irqb = '1; +assign cpu_irqb = irq_data_out == 0; addr_decode decode( .addr(cpu_addr), .ram_cs(ram_cs), .rom_cs(rom_cs), .hex_cs(hex_cs), - .uart_cs(uart_cs) + .uart_cs(uart_cs), + .irq_cs(irq_cs) ); @@ -79,6 +83,8 @@ always_comb begin cpu_data_out = rom_data_out; else if (uart_cs) cpu_data_out = uart_data_out; + else if (irq_cs) + cpu_data_out = irq_data_out; else cpu_data_out = 'x; end @@ -123,6 +129,16 @@ uart uart( .TXD(UART_TXD), .data_out(uart_data_out) ); + +always_ff @(posedge clk_50) begin + if (rst) + irq_data_out <= '0; + else if (irq_cs && ~cpu_rwb) + irq_data_out <= irq_data_out & cpu_data_in; + else if (~button_1) + irq_data_out <= {irq_data_out[7:1], ~button_1}; + +end endmodule \ No newline at end of file diff --git a/sw/Makefile b/sw/Makefile index 0927fa6..3c85514 100644 --- a/sw/Makefile +++ b/sw/Makefile @@ -22,7 +22,7 @@ OBJS+=$(patsubst %.c,%.o,$(filter %c,$(SRCS))) TEST_SRCS=$(wildcard $(TESTS)/*.s) $(wildcard $(TESTS)/*.c) TEST_OBJS+=$(patsubst %.s,%.o,$(filter %s,$(TEST_SRCS))) TEST_OBJS+=$(patsubst %.c,%.o,$(filter %c,$(TEST_SRCS))) -TEST_OBJS+=$(filter-out boot.o,$(filter-out main.o,$(OBJS))) +TEST_OBJS+=$(filter-out boot.o,$(filter-out main.o,$(filter-out vectors.o,$(OBJS)))) all: $(HEX) diff --git a/sw/boot.s b/sw/boot.s index 269c5fd..67e7b15 100644 --- a/sw/boot.s +++ b/sw/boot.s @@ -14,12 +14,6 @@ .include "zeropage.inc" -.segment "VECTORS" - -.addr _init -.addr _init -.addr _init - ; --------------------------------------------------------------------------- ; Place the startup code in a special segment @@ -49,7 +43,7 @@ _init: LDX #$FF ; Initialize stack pointer to $01FF ; --------------------------------------------------------------------------- ; Call main() - + cli JSR _main ; --------------------------------------------------------------------------- diff --git a/sw/interrupt.h b/sw/interrupt.h new file mode 100644 index 0000000..ebd5951 --- /dev/null +++ b/sw/interrupt.h @@ -0,0 +1,14 @@ +#ifndef _INTERRUPT_H +#define _INTERRUPT_H + +#include + +#define BUTTON (1 << 0) + +void irq_int(); +void nmi_int(); + +uint8_t irq_get_status(); +void irq_set_status(uint8_t); + +#endif \ No newline at end of file diff --git a/sw/interrupt.s b/sw/interrupt.s new file mode 100644 index 0000000..f87f81d --- /dev/null +++ b/sw/interrupt.s @@ -0,0 +1,58 @@ +; --------------------------------------------------------------------------- +; interrupt.s +; --------------------------------------------------------------------------- +; +; Interrupt handler. +; +; Checks for a BRK instruction and returns from all valid interrupts. + +.import _handle_irq + +.export _irq_int, _nmi_int +.export _irq_get_status, _irq_set_status + +.include "io.inc65" + +.segment "CODE" + +.PC02 ; Force 65C02 assembly mode + +; --------------------------------------------------------------------------- +; Non-maskable interrupt (NMI) service routine + +_nmi_int: RTI ; Return from all NMI interrupts + +; --------------------------------------------------------------------------- +; Maskable interrupt (IRQ) service routine + +_irq_int: PHX ; Save X register contents to stack + TSX ; Transfer stack pointer to X + PHA ; Save accumulator contents to stack + INX ; Increment X so it points to the status + INX ; register value saved on the stack + LDA $100,X ; Load status register contents + AND #$10 ; Isolate B status bit + BNE break ; If B = 1, BRK detected + +; --------------------------------------------------------------------------- +; IRQ detected, return + +irq: PLA ; Restore accumulator contents + PLX ; Restore X register contents + jsr _handle_irq ; Handle the IRQ + RTI ; Return from all IRQ interrupts + +; --------------------------------------------------------------------------- +; BRK detected, stop + +break: JMP break ; If BRK is detected, something very bad + ; has happened, so stop running + +_irq_get_status: + lda IRQ_STATUS + ldx #$00 + rts + +_irq_set_status: + sta IRQ_STATUS + rts \ No newline at end of file diff --git a/sw/io.inc65 b/sw/io.inc65 index 9192cff..f7f3155 100644 --- a/sw/io.inc65 +++ b/sw/io.inc65 @@ -4,3 +4,5 @@ UART = $7ff4 UART_TXB = UART UART_RXB = UART UART_STATUS = UART + 1 + +IRQ_STATUS = $7fff diff --git a/sw/irq.c b/sw/irq.c new file mode 100644 index 0000000..3aec0c1 --- /dev/null +++ b/sw/irq.c @@ -0,0 +1,20 @@ + +#include + +#include "interrupt.h" + +// This is defined in main.c +void puts(const char* s); + +void handle_irq() { + uint8_t status; + + puts("Interrupt Detected!\n"); + + status = irq_get_status(); + + if (status & BUTTON) { + puts("Button Interrupt!\n"); + irq_set_status(status & ~BUTTON); + } +} \ No newline at end of file diff --git a/sw/tests/test_main.c b/sw/tests/test_main.c index 50e3d85..564fea6 100644 --- a/sw/tests/test_main.c +++ b/sw/tests/test_main.c @@ -2,6 +2,7 @@ #include "sevenseg.h" #include "uart.h" +#include "interrupt.h" int main(void) { @@ -75,5 +76,14 @@ int main(void) printf("Done!\n\n"); + printf("Testing irq_get_status...\n"); + *(uint8_t*)0x7fff = 0xa5; + if (irq_get_status() != 0xa5) { + printf("Incorrect value!\n", i); + retval++; + } + printf("Done!\n\n"); + + return retval != 0; } \ No newline at end of file diff --git a/sw/vectors.s b/sw/vectors.s new file mode 100644 index 0000000..81ae6e0 --- /dev/null +++ b/sw/vectors.s @@ -0,0 +1,14 @@ +; --------------------------------------------------------------------------- +; vectors.s +; --------------------------------------------------------------------------- +; +; Defines the interrupt vector table. + +.import _init +.import _nmi_int, _irq_int + +.segment "VECTORS" + +.addr _nmi_int ; NMI vector +.addr _init ; Reset vector +.addr _irq_int ; IRQ/BRK vector \ No newline at end of file