diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 826d87d..eb2c7fa 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -145,4 +145,19 @@ mapper_code sim:
- cd hw/efinix_fpga/simulation
- make clean
- TEST_PROGRAM_NAME=mapper_test make mapper_code_tb
- - ./mapper_code_tb
\ No newline at end of file
+ - ./mapper_code_tb
+
+interrupt_controller sim:
+ tags:
+ - linux
+ - iverilog
+ stage: simulate
+ artifacts:
+ paths:
+ - hw/efinix_fpga/simulation/interrupt_controller.vcd
+ script:
+ - source init_env.sh
+ - cd hw/efinix_fpga/simulation
+ - make clean
+ - TEST_PROGRAM_NAME=mapper_test make interrupt_controller_tb
+ - ./interrupt_controller_tb
\ No newline at end of file
diff --git a/doc/pic.drawio b/doc/pic.drawio
new file mode 100644
index 0000000..d96fc65
--- /dev/null
+++ b/doc/pic.drawio
@@ -0,0 +1,405 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/hw/efinix_fpga/simulation/Makefile b/hw/efinix_fpga/simulation/Makefile
index 612d07f..2d6328d 100644
--- a/hw/efinix_fpga/simulation/Makefile
+++ b/hw/efinix_fpga/simulation/Makefile
@@ -9,6 +9,8 @@ TEST_PROGRAM_NAME?=loop_test
TEST_FOLDER?=$(REPO_TOP)/sw/test_code/$(TEST_PROGRAM_NAME)
TEST_PROGRAM?=$(REPO_TOP)/sw/test_code/$(TEST_PROGRAM_NAME)/$(TEST_PROGRAM_NAME).hex
+STANDALONE_TB= interrupt_controller_tb mapper_code_tb mapper_tb
+
#TODO implement something like sources.list
TOP_MODULE=sim_top
@@ -29,11 +31,11 @@ sim: $(TARGET)
full_sim: $(TARGET) $(SD_IMAGE)
vvp -i $(TARGET) -fst
-mapper_tb: $(SRCS) $(TBS)
+$(STANDALONE_TB): $(SRCS) $(TBS)
iverilog -g2005-sv $(FLAGS) -s $@ -o $@ $(INC) $(SRCS) $(TBS)
-mapper_code_tb: $(SRCS) $(TBS) $(INIT_MEM)
- iverilog -g2005-sv $(FLAGS) -s $@ -o $@ $(INC) $(SRCS) $(TBS)
+# mapper_code_tb: $(SRCS) $(TBS) $(INIT_MEM)
+# iverilog -g2005-sv $(FLAGS) -s $@ -o $@ $(INC) $(SRCS) $(TBS)
$(TARGET): $(INIT_MEM) $(SRCS)
@@ -55,5 +57,5 @@ clean:
rm -rf $(TARGET)
rm -rf $(INIT_MEM)
rm -rf $(SD_IMAGE)
- rm -rf mapper_tb
- rm -rf mapper_tb.vcd
+ rm -rf $(STANDALONE_TB)
+ rm -rf *.vcd
diff --git a/hw/efinix_fpga/simulation/tbs/interrupt_controller_tb.sv b/hw/efinix_fpga/simulation/tbs/interrupt_controller_tb.sv
new file mode 100644
index 0000000..a234f5f
--- /dev/null
+++ b/hw/efinix_fpga/simulation/tbs/interrupt_controller_tb.sv
@@ -0,0 +1,208 @@
+`timescale 1ns/1ps
+
+module interrupt_controller_tb();
+
+logic r_clk_cpu;
+
+localparam BITS_256 = 256'hffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
+
+// clk_cpu
+initial begin
+ r_clk_cpu <= '1;
+ forever begin
+ #125 r_clk_cpu <= ~r_clk_cpu;
+ end
+end
+
+logic reset;
+logic addr;
+logic [7:0] i_data;
+logic [7:0] o_data;
+logic cs;
+logic rwb;
+
+logic [255:0] int_in;
+logic int_out;
+
+interrupt_controller u_interrupt_controller(
+ .clk(r_clk_cpu),
+ .reset(reset),
+ .i_data(i_data),
+ .o_data(o_data),
+ .addr(addr),
+ .cs(cs),
+ .rwb(rwb),
+ .int_in(int_in),
+ .int_out(int_out)
+);
+
+/* Test Level triggered IRQ by triggering IRQ0
+ * and then clearing it,
+ */
+task test_edge_irq();
+ $display("Testing Edge IRQ");
+ do_reset();
+ set_enable(255'hff);
+ set_edge_type(255'h0);
+ set_interrupts(1);
+ assert (int_out == 1) else begin
+ errors = errors + 1;
+ $error("Interrupt should be high!");
+ end
+ send_eoi();
+ assert (int_out == 0) else begin
+ errors = errors + 1;
+ $error("Interrupt should be low!");
+ end
+ set_interrupts(0);
+ assert (int_out == 0) else begin
+ errors = errors + 1;
+ $error("Interrupt should be low!");
+ end
+endtask
+
+task test_level_irq();
+ $display("Testing level IRQ");
+ do_reset();
+ set_enable(255'hff);
+ set_edge_type(255'hff);
+ set_interrupts(1);
+ assert (int_out == 1) else begin
+ errors = errors + 1;
+ $error("Interrupt should be high!");
+ end
+ send_eoi();
+ assert (int_out == 1) else begin
+ errors = errors + 1;
+ $error("Interrupt should be high!");
+ end
+ set_interrupts(0);
+ send_eoi();
+ assert (int_out == 0) else begin
+ errors = errors + 1;
+ $error("Interrupt should be low!");
+ end
+endtask
+
+
+task test_irq_val();
+ int irq_val = -1;
+ $display("Testing IRQ val output");
+ do_reset();
+ set_enable('1);
+ set_edge_type('1);
+ for (int i = 255; i >= 0; i--) begin
+ set_interrupts(BITS_256 << i);
+ read_irqval(irq_val);
+ assert(i == irq_val) else begin
+ errors = errors + 1;
+ $display("Expected %d got %d", i, irq_val);
+ end
+ end
+
+ for (int i = 0; i < 256; i++) begin
+ set_interrupts(BITS_256 >> i);
+ read_irqval(irq_val);
+ assert(int_out == 1) else begin
+ errors = errors + 1;
+ $display("int_out should be asserted!");
+ end
+ assert(0 == irq_val) else begin
+ errors = errors + 1;
+ $display("Expected %d got %d", i, irq_val);
+ end
+ end
+
+endtask
+
+int errors;
+initial begin
+ errors = 0;
+ test_edge_irq();
+ test_level_irq();
+ test_irq_val();
+ if (errors > 0)
+ $finish_and_return(-1);
+ else
+ $finish();
+end
+
+initial
+begin
+ $dumpfile("interrupt_controller_tb.vcd");
+ $dumpvars(0,interrupt_controller_tb);
+end
+
+/* These should be shared */
+task write_reg(input logic [4:0] _addr, input logic [7:0] _data);
+ @(negedge r_clk_cpu);
+ cs <= '1;
+ addr <= _addr;
+ rwb <= '0;
+ i_data <= '1;
+ @(posedge r_clk_cpu);
+ i_data <= _data;
+ @(negedge r_clk_cpu);
+ cs <= '0;
+ rwb <= '1;
+ @(posedge r_clk_cpu);
+endtask
+
+task read_reg(input logic [2:0] _addr, output logic [7:0] _data);
+ @(negedge r_clk_cpu);
+ cs <= '1;
+ addr <= _addr;
+ rwb <= '1;
+ i_data <= '1;
+ @(posedge r_clk_cpu);
+ _data <= o_data;
+ @(negedge r_clk_cpu);
+ cs <= '0;
+ rwb <= '1;
+ @(posedge r_clk_cpu);
+endtask
+
+task do_reset();
+ repeat (5) @(posedge r_clk_cpu);
+ reset = 1;
+ cs = 0;
+ rwb = 1;
+ addr = '0;
+ i_data = '0;
+ int_in = '0;
+ repeat (5) @(posedge r_clk_cpu);
+ reset = 0;
+ repeat (5) @(posedge r_clk_cpu);
+endtask
+
+task set_enable(input logic [255:0] en);
+ for (int i = 0; i < 32; i++) begin
+ write_reg(0, 8'h20 | i);
+ write_reg(1, en[8*i +: 8]);
+ end
+endtask
+
+task set_edge_type(input logic [255:0] edge_type);
+ for (int i = 0; i < 32; i++) begin
+ write_reg(0, 8'h40 | i);
+ write_reg(1, edge_type[8*i +: 8]);
+ end
+endtask
+
+task set_interrupts(logic [255:0] ints);
+ int_in = ints;
+ @(posedge r_clk_cpu);
+endtask
+
+task send_eoi();
+ write_reg(0, 8'hff);
+ write_reg(1, 8'h01);
+endtask
+
+task read_irqval(output logic [7:0] _irq_val);
+ write_reg(0, 8'h00);
+ read_reg(1, _irq_val);
+endtask
+
+
+endmodule
diff --git a/hw/efinix_fpga/src/byte_sel_register.sv b/hw/efinix_fpga/src/byte_sel_register.sv
new file mode 100644
index 0000000..c36c5c0
--- /dev/null
+++ b/hw/efinix_fpga/src/byte_sel_register.sv
@@ -0,0 +1,31 @@
+module byte_sel_register
+#(
+ parameter DATA_WIDTH = 8,
+ parameter ADDR_WIDTH = 32
+)(
+ input i_clk,
+ input i_reset,
+ input i_write,
+ input [$clog2(ADDR_WIDTH)-1:0] i_byte_sel,
+ input [DATA_WIDTH-1:0] i_data,
+ output [DATA_WIDTH-1:0] o_data,
+ output [DATA_WIDTH*ADDR_WIDTH-1:0] o_full_data
+);
+
+logic [DATA_WIDTH*ADDR_WIDTH-1:0] r_data;
+
+assign o_data = r_data[DATA_WIDTH*i_byte_sel +: DATA_WIDTH];
+assign o_full_data = r_data;
+
+always_ff @(posedge i_clk) begin
+ if (i_reset) begin
+ r_data <= '0;
+ end else begin
+ r_data <= r_data;
+ if (i_write) begin
+ r_data[DATA_WIDTH*i_byte_sel +: DATA_WIDTH] <= i_data;
+ end
+ end
+end
+
+endmodule
\ No newline at end of file
diff --git a/hw/efinix_fpga/src/interrupt_controller.sv b/hw/efinix_fpga/src/interrupt_controller.sv
index 4e00957..7f54f31 100644
--- a/hw/efinix_fpga/src/interrupt_controller.sv
+++ b/hw/efinix_fpga/src/interrupt_controller.sv
@@ -4,26 +4,148 @@ module interrupt_controller
input reset,
input [7:0] i_data,
output logic [7:0] o_data,
+ input addr,
input cs,
input rwb,
- output logic irqb_master,
-
- input irqb0, irqb1, irqb2, irqb3,
- input irqb4, irqb5, irqb6, irqb7
+ input [255:0] int_in,
+ output logic int_out
);
+logic w_enable_write;
+logic [7:0] w_enable_data;
+logic [255:0] w_enable_full_data;
-//All of the inputs are low level triggered.
-logic [7:0] irqbv;
-assign irqbv = {irqb0, irqb1, irqb2, irqb3, irqb4, irqb5, irqb6, irqb7};
+logic [255:0] int_in_d1;
-always @(posedge clk) begin
- o_data <= irqbv;
- irqb_master = &irqbv;
+logic [4:0] w_byte_sel;
- if (cs & ~rwb) begin
- o_data <= o_data | i_data;
+logic [7:0] irq_val;
+
+byte_sel_register #(
+ .DATA_WIDTH(8),
+ .ADDR_WIDTH(32)
+) reg_enable (
+ .i_clk(~clk),
+ .i_reset(reset),
+ .i_write(w_enable_write),
+ .i_byte_sel(w_byte_sel),
+ .i_data(i_data),
+ .o_data(w_enable_data),
+ .o_full_data(w_enable_full_data)
+);
+
+logic we, re;
+
+assign we = cs & ~rwb;
+assign re = cs & rwb;
+
+logic [255:0] int_masked;
+assign int_masked = int_in & w_enable_full_data;
+
+
+logic w_type_write;
+logic [7:0] w_type_data;
+logic [255:0] w_type_full_data;
+
+byte_sel_register #(
+ .DATA_WIDTH(8),
+ .ADDR_WIDTH(32)
+) reg_type (
+ .i_clk(~clk),
+ .i_reset(reset),
+ .i_write(w_type_write),
+ .i_byte_sel(w_byte_sel),
+ .i_data(i_data),
+ .o_data(w_type_data),
+ .o_full_data(w_type_full_data)
+);
+
+logic [7:0] cmd, cmd_next;
+
+logic w_eoi;
+
+logic [255:0] r_int, r_int_next;
+
+always_comb begin
+ w_eoi = 0;
+
+ if (addr == '0 && we) begin
+ cmd_next = i_data;
+ end else begin
+ cmd_next = cmd;
+ end
+
+
+ w_type_write = '0;
+ w_enable_write = '0;
+
+ if (addr == '1) begin
+ unique casez (cmd)
+ 8'b000?????: begin
+ o_data = irq_val;
+ end
+
+ 8'b001?????: begin
+ w_enable_write = we;
+ w_byte_sel = cmd[4:0];
+ o_data = w_enable_data;
+ end
+
+ 8'b010?????: begin
+ w_type_write = we;
+ w_byte_sel = cmd[4:0];
+ o_data = w_type_data;
+ end
+
+ 8'hff: begin
+ // Kind of dumb, still requires a data write
+ w_eoi = i_data[0] & we;
+ end
+ endcase
+ end
+
+ int_out = |r_int;
+
+ irq_val = 8'hff;
+ for (int i = 255; i >= 0; i--) begin
+ if (r_int[i] == 1) begin
+ irq_val = i;
+ end
+ end
+
+ for (int i = 0; i < 256; i++) begin
+ case (w_type_full_data[i])
+ 0: begin // Edge triggered
+ if (w_eoi && i == irq_val) begin
+ r_int_next[i] = 0;
+ end else begin
+ r_int_next[i] = (~int_in_d1[i] & int_masked[i]) | r_int[i];
+ end
+ end
+
+ 1: begin // Level Triggered
+ // If we are trying to clear this interrupt but it is still active,
+ // then we don't actually want to clear it.
+ if (w_eoi && i == irq_val) begin
+ r_int_next[i] = int_masked[i];
+ end else begin
+ r_int_next[i] = r_int[i] | int_masked[i];
+ end
+ end
+ endcase
+ end
+end
+
+always_ff @(negedge clk) begin
+ if (reset) begin
+ r_int <= '0;
+ cmd <= '0;
+ int_in_d1 <= '0;
+ end else begin
+ r_int <= r_int_next;
+ cmd <= cmd_next;
+ int_in_d1 <= int_in;
end
end
diff --git a/hw/efinix_fpga/src/super6502.sv b/hw/efinix_fpga/src/super6502.sv
index 66fd0fd..f296019 100644
--- a/hw/efinix_fpga/src/super6502.sv
+++ b/hw/efinix_fpga/src/super6502.sv
@@ -267,16 +267,7 @@ interrupt_controller u_interrupt_controller(
.i_data(cpu_data_in),
.o_data(w_irq_data_out),
.cs(w_irq_cs),
- .rwb(cpu_rwb),
- .irqb_master(cpu_irqb),
- .irqb0(w_timer_irqb),
- .irqb1('1),
- .irqb2('1),
- .irqb3('1),
- .irqb4('1),
- .irqb5('1),
- .irqb6('1),
- .irqb7('1)
+ .rwb(cpu_rwb)
);
diff --git a/hw/efinix_fpga/super6502.xml b/hw/efinix_fpga/super6502.xml
index 013bb59..709d4f1 100644
--- a/hw/efinix_fpga/super6502.xml
+++ b/hw/efinix_fpga/super6502.xml
@@ -1,5 +1,5 @@
-
+
@@ -20,6 +20,7 @@
+