Add timer and testbench
This commit is contained in:
1
hw/efinix_fpga/.gitignore
vendored
1
hw/efinix_fpga/.gitignore
vendored
@@ -5,4 +5,5 @@ outflow
|
|||||||
*.log*
|
*.log*
|
||||||
*.vcd
|
*.vcd
|
||||||
*.gtkw
|
*.gtkw
|
||||||
|
*.vvp
|
||||||
|
|
||||||
|
|||||||
75
hw/efinix_fpga/simulation/timer_tb.sv
Normal file
75
hw/efinix_fpga/simulation/timer_tb.sv
Normal file
@@ -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
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<efx:project name="super6502" description="" last_change_date="Fri December 23 2022 12:06:20" location="/home/byron/Projects/super6502/hw/efinix_fpga" sw_version="2022.1.226" last_run_state="pass" last_run_tool="efx_pgm" last_run_flow="bitstream" config_result_in_sync="true" design_ood="sync" place_ood="sync" route_ood="sync" xmlns:efx="http://www.efinixinc.com/enf_proj" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.efinixinc.com/enf_proj enf_proj.xsd">
|
<efx:project name="super6502" description="" last_change_date="Thu December 29 2022 11:13:49" location="/home/byron/Projects/super6502/hw/efinix_fpga" sw_version="2022.1.226" last_run_state="pass" last_run_tool="efx_pgm" last_run_flow="bitstream" config_result_in_sync="true" design_ood="new" place_ood="sync" route_ood="sync" xmlns:efx="http://www.efinixinc.com/enf_proj" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.efinixinc.com/enf_proj enf_proj.xsd">
|
||||||
<efx:device_info>
|
<efx:device_info>
|
||||||
<efx:family name="Trion"/>
|
<efx:family name="Trion"/>
|
||||||
<efx:device name="T20F256"/>
|
<efx:device name="T20F256"/>
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
<efx:design_file name="leds.sv" version="default" library="default"/>
|
<efx:design_file name="leds.sv" version="default" library="default"/>
|
||||||
<efx:design_file name="addr_decode.sv" version="default" library="default"/>
|
<efx:design_file name="addr_decode.sv" version="default" library="default"/>
|
||||||
<efx:design_file name="sdram_adapter.sv" version="default" library="default"/>
|
<efx:design_file name="sdram_adapter.sv" version="default" library="default"/>
|
||||||
|
<efx:design_file name="timer.sv" version="default" library="default"/>
|
||||||
<efx:top_vhdl_arch name=""/>
|
<efx:top_vhdl_arch name=""/>
|
||||||
</efx:design_info>
|
</efx:design_info>
|
||||||
<efx:constraint_info>
|
<efx:constraint_info>
|
||||||
|
|||||||
149
hw/efinix_fpga/timer.sv
Normal file
149
hw/efinix_fpga/timer.sv
Normal file
@@ -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
|
||||||
Reference in New Issue
Block a user