diff --git a/hw/efinix_fpga/.gitignore b/hw/efinix_fpga/.gitignore index 1a2cda5..ef0d6e8 100644 --- a/hw/efinix_fpga/.gitignore +++ b/hw/efinix_fpga/.gitignore @@ -5,4 +5,5 @@ outflow *.log* *.vcd *.gtkw +*.vvp diff --git a/hw/efinix_fpga/simulation/timer_tb.sv b/hw/efinix_fpga/simulation/timer_tb.sv new file mode 100644 index 0000000..24d6cb2 --- /dev/null +++ b/hw/efinix_fpga/simulation/timer_tb.sv @@ -0,0 +1,75 @@ +module sim(); + +timeunit 10ns; +timeprecision 1ns; + +logic clk; +logic rwb; +logic clk_50; +logic reset; + +logic [2:0] addr; +logic [7:0] i_data; +logic [7:0] o_data; +logic cs; +logic irq; + +timer dut( + .*); + +always #1 clk_50 = clk_50 === 1'b0; +always #100 clk = clk === 1'b0; + +task write_reg(input logic [2:0] _addr, input logic [7:0] _data); + @(negedge clk); + cs <= '1; + addr <= _addr; + rwb <= '0; + i_data <= '1; + @(posedge clk); + i_data <= _data; + @(negedge clk); + cs <= '0; + rwb <= '1; +endtask + +task read_reg(input logic [2:0] _addr, output logic [7:0] _data); + @(negedge clk); + cs <= '1; + addr <= _addr; + rwb <= '1; + i_data <= '1; + @(posedge clk); + _data <= o_data; + @(negedge clk); + cs <= '0; + rwb <= '1; +endtask + +initial +begin + $dumpfile("timer.vcd"); + $dumpvars(0,sim); +end + +logic [7:0] read_data; + +initial begin + reset <= '1; + repeat(5) @(posedge clk); + reset <= '0; + + write_reg(5, 16); + + repeat(1024) @(posedge clk); + + repeat(10) begin + read_reg(0, read_data); + $display("Read: %d", read_data); + repeat(1024) @(posedge clk); + end + $finish(); + +end + +endmodule diff --git a/hw/efinix_fpga/super6502.xml b/hw/efinix_fpga/super6502.xml index a84460b..81d0831 100644 --- a/hw/efinix_fpga/super6502.xml +++ b/hw/efinix_fpga/super6502.xml @@ -1,5 +1,5 @@ - + @@ -16,6 +16,7 @@ + diff --git a/hw/efinix_fpga/timer.sv b/hw/efinix_fpga/timer.sv new file mode 100644 index 0000000..05c0f51 --- /dev/null +++ b/hw/efinix_fpga/timer.sv @@ -0,0 +1,149 @@ +module timer +( + input clk, + input reset, + input [7:0] i_data, + output logic [7:0] o_data, + input cs, + input rwb, + input [2:0] addr, + output logic irq +); + +logic [16:0] tick_counter_reg, irq_counter_reg; +logic [7:0] divisor, status, control; + +// -------------------------------- +// | 0 | Tick Counter Low | +// -------------------------------- +// | 1 | Tick Counter High | +// -------------------------------- +// | 2 | IRQ Counter Low | +// -------------------------------- +// | 3 | IRQ Counter High | +// -------------------------------- +// | 4 | Reserved | +// -------------------------------- +// | 5 | Divisor | +// -------------------------------- +// | 6 | Status | +// -------------------------------- +// | 7 | Control | +// -------------------------------- + + +// Tick counter register +// The tick counter register is read only. It starts at 0 upon +// reset and increments continuously according to the divsor. + +// IRQ Counter Register +// The IRQ counter register is writable, which is how you set the desired +// time to count down. Writing to the high register does nothing, while +// writing to the low register will begin the countdown. Based on the control +// register, the register will reset itself when it reaches 0 and triggers an +// interrupt. See the control register for more details. + +// Divisor +// The divisor register controls how fast the timer counts up. The divisor is +// bit shifted left by 8 (multiplied by 256), and that is the number of pulses +// it takes to increment the counters. + +// Status +// 6:0 Reserved +// 7: Interrupt. Set if an interrupt has occured. Write to clear. + +// Control +// 0: Oneshot. Set if you only want the timer to run once. +// 7:1 Reserved + + +// What features do we want for the timer? +// 1. Tracking elapsed time +// 2. Trigger interrupts (repeated or elapsed) + +// General Idea +// Takes in the input clock and can set a divisor +// of a power of 2. Every time that many clock pulses +// occur, it will increment the counter. The counter +// can then be read at any point. +// The interrupts will have a difference counter which +// counts down. When the counter reaches 0, it will trigger +// an interrupt and optionally reset the counter to start +// again. + +logic [15:0] pulsecount; + +logic [15:0] tickcount; + +//I think this should be negedge so that writes go through +always @(negedge clk) begin + if (reset) begin + tickcount <= '0; + pulsecount <= '0; + tick_counter_reg <= '0; + irq_counter_reg <= '0; + divisor <= '0; + status <= '0; + control <= '0; + end else begin + + if (pulsecount[15:8] == divisor) begin + tickcount <= tickcount + 16'b1; + pulsecount <= '0; + end else begin + pulsecount <= pulsecount + 16'b1; + end + + if (cs & ~rwb) begin + case (addr) + 3'h5: begin + divisor <= i_data; + end + endcase + end + + end +end + +always_comb begin + o_data = '0; + + unique case (addr) + 3'h0: begin + o_data = tickcount[7:0]; + end + + 3'h1: begin + o_data = tickcount[15:8]; + end + + 3'h2: begin + + end + + 3'h3: begin + + end + + 3'h4: begin + + end + + 3'h5: begin + + end + + 3'h6: begin + + end + + 3'h7: begin + + end + + endcase +end + + + +endmodule